I am trying to handle event in OxyPlot using EventTrigger. But it does not work like I want.
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:oxy="http://oxyplot.org/wpf"
Title="MainWindow" Height="350" Width="525">
<Grid>
<oxy:PlotView Model="{Binding PlotModel}" DefaultTrackerTemplate="{x:Null}">
<i:Interaction.Triggers >
<i:EventTrigger EventName="MouseDown">
<i:InvokeCommandAction Command="{Binding Move}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</oxy:PlotView>
</Grid>
And code look's like
using OxyPlot;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using OxyPlot.Axes;
using GalaSoft.MvvmLight.Command;
namespace WpfApplication14
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public partial class MainWindow : Window
{
public class DataGeneration
{
public PlotModel PlotModel { get; set; }
public ICommand Move { get; set; }
public int _NumberOf;
public Dictionary<int, List<int>> Test { get; set; }
OxyPlot.Series.LineSeries LS;
Random rnd;
LinearAxis LA;
public int EndPosition = 0;
public DataGeneration(int NumberOf)
{
LA = new LinearAxis()
{
Position=AxisPosition.Bottom,
Minimum=0,
Maximum = EndPosition
};
Move = new RelayCommand(() => TestMove());
_NumberOf = NumberOf;
rnd = new Random();
Test = new Dictionary<int, List<int>>();
PlotModel = new PlotModel();
PlotModel.Axes.Add(LA);
LS = new OxyPlot.Series.LineSeries();
PlotModel.Series.Add(LS);
}
public void TestMove()
{
PlotModel.Axes[0].Reset();
}
public void AddPoint()
{
do
{
LS.Points.Add(new DataPoint(++_NumberOf, rnd.Next(1, 10)));
System.Threading.Thread.Sleep(1000);
EndPosition += 1;
LA.Maximum = EndPosition+3;
LA.Minimum = EndPosition;
Update();
} while (true);
}
public void Update()
{
PlotModel.InvalidatePlot(true);
}
}
public delegate void BeginUpdate();
private Stopwatch stopwatch = new Stopwatch();
private long lastUpdateMilliSeconds;
DataGeneration Data;
public MainWindow()
{
Data = new DataGeneration(1);
BeginUpdate BU = new BeginUpdate(Data.AddPoint);
IAsyncResult result = BU.BeginInvoke(null, null);
this.DataContext = Data;
CompositionTarget.Rendering += CompositionTargetRendering;
InitializeComponent();
}
private void CompositionTargetRendering(object sender, EventArgs e)
{
if (stopwatch.ElapsedMilliseconds > lastUpdateMilliSeconds + 300)
{
Data.Update();
}
}
}
}
why it firing when I make double click on OxyPlot? Genaraly i am trying to pan chart by left mousebutton.It's implemented in oxyplot by default(right mouse button).
Related
How would you add a placeholder to an Editor in Xamarin Forms for iOS?
I tried adding through custom renderer as Control.Layer but could not find a property related to it.
Please help.
Try following code:
PCL:
using System;
using Xamarin.Forms;
namespace ABC.CustomViews
{
public class PlaceholderEditor : Editor
{
public static readonly BindableProperty PlaceholderProperty =
BindableProperty.Create<PlaceholderEditor, string>(view => view.Placeholder, String.Empty);
public PlaceholderEditor()
{
}
public string Placeholder
{
get
{
return (string)GetValue(PlaceholderProperty);
}
set
{
SetValue(PlaceholderProperty, value);
}
}
}
}
iOS (CustomeRenderer) :
using UIKit;
using ABC.CustomViews;
using ABC.iOS.CustomRenderer;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(PlaceholderEditor), typeof(PlaceholderEditorRenderer))]
namespace ABC.iOS.CustomRenderer
{
public class PlaceholderEditorRenderer : EditorRenderer
{
private string Placeholder { get; set; }
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
var element = this.Element as PlaceholderEditor;
if (Control != null && element != null)
{
Placeholder = element.Placeholder;
Control.TextColor = UIColor.LightGray;
Control.Text = Placeholder;
Control.ShouldBeginEditing += (UITextView textView) =>
{
if (textView.Text == Placeholder)
{
textView.Text = "";
textView.TextColor = UIColor.Black; // Text Color
}
return true;
};
Control.ShouldEndEditing += (UITextView textView) =>
{
if (textView.Text == "")
{
textView.Text = Placeholder;
textView.TextColor = UIColor.LightGray; // Placeholder Color
}
return true;
};
}
}
}
}
Usage :
_replyEditor = new PlaceholderEditor
{
Placeholder = "Placeholder Text"
};
below is the code for android
using System;
using MyApp;
using MyApp.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace MyApp.Droid
{
public class CustomEditorRenderer : EditorRenderer
{
public CustomEditorRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var element = e.NewElement as CustomEditor;
this.Control.Hint = element.Placeholder;
Control.Gravity = Android.Views.GravityFlags.Start;
Control.SetBackgroundColor(global::Android.Graphics.Color.White);
Control.SetPadding(15,15,15,15);
}
}
}
Here is a Xamarin forms version that allows a placehodler to be included in the initializer of the Editor, and also handles consistent behavior if the Text property is set in code (i.e. if Editor.Text="" it will show the placeholder in light gray.
using System;
using Xamarin.Forms;
namespace CrowdWisdom.Controls
{
public class EditorPlaceHolder : Editor
{
String placeHolderText = "";
public EditorPlaceHolder(String placeholder)
{
Text = placeholder;
TextColor = Color.LightGray;
this.Focused += EditorPlaceHolder_Focused;
this.Unfocused += EditorPlaceHolder_Unfocused;
this.placeHolderText = placeholder;
}
private void EditorPlaceHolder_Focused(object sender, FocusEventArgs e) //triggered when the user taps on the Editor to interact with it
{
if (Empty())
{
base.Text = "";
this.TextColor = Color.Black;
}
}
private void EditorPlaceHolder_Unfocused(object sender, FocusEventArgs e) //triggered when the user taps "Done" or outside of the Editor to finish the editing
{
if (Empty()) //if there is text there, leave it, if the user erased everything, put the placeholder Text back and set the TextColor to gray
{
this.Text = placeHolderText;
this.TextColor = Color.LightGray;
}
}
public String PlaceHolderText
{
get
{
return this.placeHolderText;
}
set
{
if (this.Empty())
{
this.Text = value;
this.TextColor = Color.LightGray;
}
this.placeHolderText = value;
}
}
public bool Empty()
{
return (this.Text.Equals("") || this.Text.Equals(this.placeHolderText));
}
public virtual new string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
if (Empty())
{
this.TextColor = Color.LightGray;
base.Text = this.placeHolderText;
}
else
{
this.TextColor = Color.Black;
}
}
}
}
}
Xamarin Forms adding a placeholder to an Editor for Android
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace MyCare.Renderers
{
class PlaceholderEditor : Editor
{
public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create<PlaceholderEditor, string>(view => view.Placeholder, String.Empty);
public PlaceholderEditor()
{
}
public string Placeholder
{
get
{
return (string)GetValue(PlaceholderProperty);
}
set
{
SetValue(PlaceholderProperty, value);
}
}
}
}
//Renderer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using MyCare.Renderers;
using MyCare.Droid.Renderers;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(PlaceholderEditor), typeof(PlaceholderEditorRenderer))]
namespace MyCare.Droid.Renderers
{
class PlaceholderEditorRenderer : EditorRenderer
{
private string Placeholder { get; set; }
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
var element = this.Element as PlaceholderEditor;
if (Control != null && element != null)
{
Placeholder = element.Placeholder;
Control.SetTextColor(Android.Graphics.Color.Black);
Control.SetHintTextColor(Android.Graphics.Color.LightGray);
Control.Hint = element.Placeholder;
Control.SetBackgroundColor(Android.Graphics.Color.Transparent);
}
}
}
}
Inherit from Jay's solution. Here is a usage in XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cstCtrl="clr-namespace:TeamCollaXform.Views"
x:Class="TeamCollaXform.Views.MailComposePage">
...
<cstCtrl:PlaceholderEditor Grid.Row="2" x:Name="txtContent" Text="" HeightRequest="750" Placeholder="Compose..."/>
I've tried to Develop a Web Part that displays the text entered in the Property of a custom EditorPart,
but i have problem with the Properties persistence When i click on OK or Save and open the WebPart Properties the values of the properties revert to default,
same thing when i save the Page after editing the WebPart Properties and clicking on Apply OR OK buttons. Below is the code i implemented :
using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.Collections.Generic;
using Microsoft.SharePoint.Administration;
namespace SH_Perso.WebPartPerso
{
[ToolboxItemAttribute(false)]
public class WebPartPerso : System.Web.UI.WebControls.WebParts.WebPart
{
[WebBrowsable(false), Personalizable(PersonalizationScope.User), DefaultValue("PERSO")]
public string MyPortalChoice { get; set; }
[WebBrowsable(false), Personalizable(PersonalizationScope.User), DefaultValue("Documents")]
public string MyAppChoice { get; set; }
[WebBrowsable(false), Personalizable(PersonalizationScope.User), DefaultValue("Tous les éléments")]
public string MyViewChoice { get; set; }
protected override void CreateChildControls()
{
base.CreateChildControls();
Controls.Add(new LiteralControl("PORTAIL :" + MyPortalChoice + "<br/> APPLICATION : " + MyAppChoice + "<br/> VUE : " + MyViewChoice));
}
/// <summary>
/// Creates custom editor parts here and assigns a unique id to each part
/// </summary>
/// <returns>All custom editor parts used by this web part</returns>
public override EditorPartCollection CreateEditorParts()
{
PEditorPart editorPart = new PEditorPart();
editorPart.Title = "Choix de la liste à afficher";
editorPart.ID = ID + "_editorPart";
EditorPartCollection editors = new EditorPartCollection(base.CreateEditorParts(), new EditorPart[] { editorPart });
return editors;
}
public override object WebBrowsableObject
{
get
{
return(this);
}
}
}
}
// THE EDITOR PART CLASS
class PEditorPart : EditorPart
{
public TextBox PortalChoices;
public TextBox AppChoices;
public TextBox ListViews;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
PortalChoices = new TextBox();
AppChoices = new TextBox();
ListViews = new TextBox();
}
protected override void CreateChildControls()
{
Controls.Add(new LiteralControl("<div> <span style='font-weight:bold;'>Portail</span> "));
Controls.Add(PortalChoices);
Controls.Add(new LiteralControl("</div>"));
Controls.Add(new LiteralControl("<div> <span style='font-weight:bold;'>Listes disponibles</span> "));
Controls.Add(AppChoices);
Controls.Add(new LiteralControl("</div>"));
Controls.Add(new LiteralControl("<div> <span style='font-weight:bold;'>Listes des vues disponibles</span> "));
Controls.Add(ListViews);
Controls.Add(new LiteralControl("</div>"));
base.CreateChildControls();
ChildControlsCreated = true;
}
/// <summary>
/// Applies change in editor part ddl to the parent web part
/// </summary>
/// <returns></returns>
public override bool ApplyChanges()
{
try
{
EnsureChildControls();
WebPartPerso ParentWebPart = (WebPartPerso)WebPartToEdit;
if (ParentWebPart != null)
{
ParentWebPart.MyAppChoice = AppChoices.Text;
ParentWebPart.MyViewChoice = ListViews.Text;
ParentWebPart.MyPortalChoice = PortalChoices.Text;
}
ParentWebPart.SaveChanges();
// The operation was succesful
return true;
}
catch
{
// Because an error has occurred, the SyncChanges() method won’t be invoked.
return false;
}
}
/// <summary>
/// Reads current value from parent web part and show that in the ddl
/// </summary>
public override void SyncChanges()
{
EnsureChildControls();
WebPartPerso ParentWebPart = (WebPartPerso)WebPartToEdit;
if (ParentWebPart != null)
{
AppChoices.Text = ParentWebPart.MyAppChoice;
PortalChoices.Text = ParentWebPart.MyPortalChoice;
ListViews.Text = ParentWebPart.MyViewChoice.ToString();
}
}
}
Make WebBrowsable true
[WebBrowsable(true), Personalizable(PersonalizationScope.User), DefaultValue("PERSO")]
public string MyPortalChoice { get; set; }
I have implemented a helper script using a youtube example. But when I use the Resource method in my other scripts the compiler gives an null Reference error. What am I doing wrong.
this is my helper script and how the resources are called.
using UnityEngine;
using System.Collections;
namespace Helper
{
public class Resource
{
public static string AnimatorController = "System/PlayerAnimator";
}
}
The path according to my scene is
Assets/Resources/System/PlayerAnimator.controller
This is how I used in my other scripts to load the resources
private Animator _animator;
private RuntimeAnimatorController _animatorController;
_animatorController = Resources.Load(Resource.AnimatorController) as RuntimeAnimatorController;
_animator.runtimeAnimatorController = _animatorController;
What is the fix for this?
I added both of the scripts. Non didn't work
PlayerCharctor.cs
using UnityEngine; using System.Collections; using Helper;
[RequireComponent(typeof(NetworkView))] [RequireComponent(typeof(CharacterController))] [RequireComponent(typeof(Animator))]
[RequireComponent(typeof(PlayerMotor))] [RequireComponent(typeof(PlayerCamera))]
[AddComponentMenu("Scripts/Player/PlayerCharacter")] public class PlayerCharacter : MonoBehaviour {
#region Variables & Properties (Private)
private CharacterController _controller;
private Animator _animator;
private RuntimeAnimatorController _animatorController;
#endregion
#region Variables & Properties (Public)
public CharacterController Controller
{
get
{
return _controller;
}
}
public Animator Animator
{
get
{
return _animator;
}
}
#endregion
#region Unity Event Funtions
void awake()
{
_animator = GetComponent<Animator>();
_controller = GetComponent<CharacterController>();
}
// Use this for initialization
void Start ()
{
//Ensure networkview Component exists
if (GetComponent<NetworkView>() != null)
{
//Ensure that initialization only executes if this is a valid instance
if (GetComponent<NetworkView>().isMine || Network.peerType == NetworkPeerType.Disconnected)
{
//Load in the AnimatorController at runtime
_animatorController = Resources.Load(Resource.AnimatorController) as RuntimeAnimatorController;
_animator.runtimeAnimatorController = _animatorController;
_controller.center = new Vector3(0f, 1f, 0f);
_controller.height = 1.8f;
}
else
{
enabled = false;
}
}
else
{
Debug.Log("Attach a NetWorkViewComponent!!");
}
}
// Update is called once per frame
void Update ()
{
}
#endregion
#region Methods
#endregion Methods }
AND the Helper script
using UnityEngine;
using System.Collections;
namespace Helper
{
#region Referance Cache
public class PlayerInput
{
public static string Horizontal = "Horizontal";
public static string Vertical = "vertical";
public static string Jump = "Jump";
public static string RightX = "Mouse X";
public static string RightY = "Mouse Y";
}
public class GameTag
{
//System tags
public static string Untagged = "Untagged";
public static string Respawn = "Respawn";
public static string Finish = "Finish";
public static string EditorOnly = "EditorOnly";
public static string MainCamera = "MainCamera";
public static string Player = "Player";
public static string GameController = "GameController";
public static string PlayerCamera = "PlayerCamera";
}
public class Resource
{
public static string AnimatorController= "System/PLController"; //Changed the name of the controller (new one)
}
public static class AnimatorConditions
{
public static string Speed = "Speed";
public static string Direction = "Direction";
public static string Grounded = "Grounded";
public static string AirVelocity = "AirVelocity";
}
#endregion
#region FSM Enumerations (Finite State Machine)
public enum CameraState
{
Normal,
Target
}
public enum SpeedState
{
Walk,
Run,
Sprint
}
#endregion
#region Object Structures
public struct CameraTargetObject
{
private Vector3 position;
private Transform xForm;
public Vector3 Position
{
get
{
return position;
}
set
{
position = value;
}
}
public Transform XForm
{
get
{
return xForm;
}
set
{
xForm = value;
}
}
public void Init(string camName, Vector3 pos, Transform transform, Transform parent)
{
position = pos;
xForm = transform;
xForm.name = camName;
xForm.parent = parent;
xForm.localPosition = Vector3.zero;
xForm.localPosition = position;
}
}
public struct CameraMountPoint
{
private Vector3 position;
private Transform xForm;
public Vector3 Position
{
get
{
return position;
}
set
{
position = value;
}
}
public Transform XForm
{
get
{
return xForm;
}
set
{
xForm = value;
}
}
public void Init(string camName, Vector3 pos, Transform transform, Transform parent)
{
position = pos;
xForm = transform;
xForm.name = camName;
xForm.parent = parent;
xForm.localPosition = Vector3.zero;
xForm.localPosition = position;
}
}
#endregion
}
There is no problem in your code of loading assets from Resources. But in fact it may be because of your _animator variable. I am 99% sure that it is null. First get the Animator component like _animator = GetComponent<Animator>(); then assign its RuntimeAnimatorController.
I'm experiencing with the UICollectionView(Controller) for the first time. Actually it should be as simple as working with TableViews, it's not though.
Rather than showing all images in a flow (several rows) just the top row is displayed. All the other images are somewhere... scrolling is enabled but nothing happens, no bouncing, no scrolling, ...
And after an orientation change (and back) some more images are visible but they appear randomly. After every orientation change they appear at an other location.
My example should have 7 images.
My properties in IB:
First time:
After rotating (and back):
And my source code to implement the photo gallery.
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Collections.Generic;
using Xamarin.Media;
using MonoTouch.AssetsLibrary;
using MonoTouch.CoreGraphics;
using System.Diagnostics;
using System.Linq;
using System.Drawing;
namespace B2.Device.iOS
{
public partial class TagesRapportDetailRegieBilderCollectionViewController : UICollectionViewController
{
private const string Album = "Rapport";
public TagesRapportDetailRegieBilderSource Source { get; private set; }
private TagesRapportDetailRegieBilderDelegate _delegate;
public TagesRapportDetailRegieBilderCollectionViewController (IntPtr handle) : base (handle)
{
Source = new TagesRapportDetailRegieBilderSource(this);
_delegate = new TagesRapportDetailRegieBilderDelegate(this);
// Delegate - Muss im konstruktor sein. ViewDidLoad geht nicht!
CollectionView.Delegate = _delegate;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Cell Klasse registrieren
CollectionView.RegisterClassForCell (typeof(ImageCell), new NSString("imageCell"));
// DataSource
CollectionView.Source = Source;
// Bilder laden
LoadImages();
}
private void LoadImages()
{
Source.Images.Clear();
var assetsLibrary = new ALAssetsLibrary();
assetsLibrary.Enumerate(ALAssetsGroupType.Album, GroupsEnumeration, GroupsEnumerationFailure);
}
private void GroupsEnumeration(ALAssetsGroup group, ref bool stop)
{
if (group != null && group.Name == Album)
{
//notifies the library to keep retrieving groups
stop = false;
//set here what types of assets we want,
//photos, videos or both
group.SetAssetsFilter(ALAssetsFilter.AllPhotos);
//start the asset enumeration
//with ALAssetsGroup's Enumerate method
group.Enumerate(AssetsEnumeration);
CollectionView.ReloadData();
}
}
private void AssetsEnumeration(ALAsset asset, int index, ref bool stop)
{
if (asset != null)
{
//notifies the group to keep retrieving assets
stop = false;
//use the asset here
var image = new UIImage(asset.AspectRatioThumbnail());
Source.Images.Add(image);
}
}
private void GroupsEnumerationFailure(NSError error)
{
if (error != null)
{
new UIAlertView("Zugriff verweigert", error.LocalizedDescription, null, "Schliessen", null).Show();
}
}
}
public class TagesRapportDetailRegieBilderDelegate : UICollectionViewDelegateFlowLayout
{
private TagesRapportDetailRegieBilderCollectionViewController _controller;
public TagesRapportDetailRegieBilderDelegate(TagesRapportDetailRegieBilderCollectionViewController controller)
{
_controller = controller;
}
public override System.Drawing.SizeF GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
var size = _controller.Source.Images[indexPath.Row].Size.Width > 0
? _controller.Source.Images[indexPath.Row].Size : new SizeF(100, 100);
size.Width /= 3;
size.Height /= 3;
return size;
}
public override UIEdgeInsets GetInsetForSection(UICollectionView collectionView, UICollectionViewLayout layout, int section)
{
return new UIEdgeInsets(50, 20, 50, 20);
}
}
public class TagesRapportDetailRegieBilderSource : UICollectionViewSource
{
private TagesRapportDetailRegieBilderCollectionViewController _controller;
public List<UIImage> Images { get; set; }
public TagesRapportDetailRegieBilderSource(TagesRapportDetailRegieBilderCollectionViewController controller)
{
_controller = controller;
Images = new List<UIImage>();
}
public override int NumberOfSections(UICollectionView collectionView)
{
return 1;
}
public override int GetItemsCount(UICollectionView collectionView, int section)
{
return Images.Count;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.DequeueReusableCell(new NSString("imageCell"), indexPath) as ImageCell;
cell.Image = Images[indexPath.Row];
return cell;
}
}
public class ImageCell : UICollectionViewCell
{
UIImageView imageView;
[Export ("initWithFrame:")]
public ImageCell (System.Drawing.RectangleF frame) : base (frame)
{
imageView = new UIImageView(frame);
imageView.AutoresizingMask = ~UIViewAutoresizing.None;
ContentView.AddSubview (imageView);
}
public UIImage Image
{
set
{
imageView.Image = value;
}
}
}
}
If all you want to do is displaying the cells in an uniform grid, overriding GetSizeForItem shouldn't be necessary in the first place. Just configure the cellsize property of your flow layout either in IB or programatically during ViewDidLoad and be done with it.
There's another problem with your code:
group.Enumerate(AssetsEnumeration)
This will run asynchronously. That means:
CollectionView.ReloadData();
Will only cover a small subset of your images. It would be better to issue a ReloadData() when group == null, which indicates that the enumeration is complete.
You could also avoid ReloadData alltogether and call CollectionView.InsertItem() everytime you've added an image. This has the benefit of your items become immediately visible instead of all of them at once after everything has been enumerated - which may take some time (on the device). The downside is that you've got to be careful to not run into this.
We notice that inside of our .Net application we have contention when it comes to using SqlDataReader. While we understand that SqlDataReader is not ThreadSafe, it should scale. The following code is a simple example to show that we cannot scale our application because there is contention on the SqlDataReader GetValue method. We are not bound by CPU, Disk, or Network; Just the internal contention on the SqlDataReader. We can run the application 10 times with 1 thread and it scales linearly, but 10 threads in 1 app does not scale. Any thoughts on how to scale reading from SQL Server in a single c# application?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Globalization;
namespace ThreadAndSQLTester
{
class Host
{
/// <summary>
/// Gets or sets the receive workers.
/// </summary>
/// <value>The receive workers.</value>
internal List<Worker> Workers { get; set; }
/// <summary>
/// Gets or sets the receive threads.
/// </summary>
/// <value>The receive threads.</value>
internal List<Thread> Threads { get; set; }
public int NumberOfThreads { get; set; }
public int Sleep { get; set; }
public int MinutesToRun { get; set; }
public bool IsRunning { get; set; }
private System.Timers.Timer runTime;
private object lockVar = new object();
public Host()
{
Init(1, 0, 0);
}
public Host(int numberOfThreads, int sleep, int minutesToRun)
{
Init(numberOfThreads, sleep, minutesToRun);
}
private void Init(int numberOfThreads, int sleep, int minutesToRun)
{
this.Workers = new List<Worker>();
this.Threads = new List<Thread>();
this.NumberOfThreads = numberOfThreads;
this.Sleep = sleep;
this.MinutesToRun = minutesToRun;
SetUpTimer();
}
private void SetUpTimer()
{
if (this.MinutesToRun > 0)
{
this.runTime = new System.Timers.Timer();
this.runTime.Interval = TimeSpan.FromMinutes(this.MinutesToRun).TotalMilliseconds;
this.runTime.Elapsed += new System.Timers.ElapsedEventHandler(runTime_Elapsed);
this.runTime.Start();
}
}
void runTime_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.runTime.Stop();
this.Stop();
this.IsRunning = false;
}
public void Start()
{
this.IsRunning = true;
Random r = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < this.NumberOfThreads; i++)
{
string threadPoolId = Math.Ceiling(r.NextDouble() * 10).ToString();
Worker worker = new Worker("-" + threadPoolId); //i.ToString());
worker.Sleep = this.Sleep;
this.Workers.Add(worker);
Thread thread = new Thread(worker.Work);
worker.Name = string.Format("WorkerThread-{0}", i);
thread.Name = worker.Name;
this.Threads.Add(thread);
thread.Start();
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Started new Worker Thread. Total active: {0}", i + 1));
}
}
public void Stop()
{
if (this.Workers != null)
{
lock (lockVar)
{
for (int i = 0; i < this.Workers.Count; i++)
{
//Thread thread = this.Threads[i];
//thread.Interrupt();
this.Workers[i].IsEnabled = false;
}
for (int i = this.Workers.Count - 1; i >= 0; i--)
{
Worker worker = this.Workers[i];
while (worker.IsRunning)
{
Thread.Sleep(32);
}
}
foreach (Thread thread in this.Threads)
{
thread.Abort();
}
this.Workers.Clear();
this.Threads.Clear();
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Threading;
using System.ComponentModel;
using System.Data.OleDb;
namespace ThreadAndSQLTester
{
class Worker
{
public bool IsEnabled { get; set; }
public bool IsRunning { get; set; }
public string Name { get; set; }
public int Sleep { get; set; }
private string dataCnString { get; set; }
private string logCnString { get; set; }
private List<Log> Logs { get; set; }
public Worker(string threadPoolId)
{
this.Logs = new List<Log>();
SqlConnectionStringBuilder cnBldr = new SqlConnectionStringBuilder();
cnBldr.DataSource = #"trgcrmqa3";
cnBldr.InitialCatalog = "Scratch";
cnBldr.IntegratedSecurity = true;
cnBldr.MultipleActiveResultSets = true;
cnBldr.Pooling = true;
dataCnString = GetConnectionStringWithWorkStationId(cnBldr.ToString(), threadPoolId);
cnBldr = new SqlConnectionStringBuilder();
cnBldr.DataSource = #"trgcrmqa3";
cnBldr.InitialCatalog = "Scratch";
cnBldr.IntegratedSecurity = true;
logCnString = GetConnectionStringWithWorkStationId(cnBldr.ToString(), string.Empty);
IsEnabled = true;
}
private string machineName { get; set; }
private string GetConnectionStringWithWorkStationId(string connectionString, string connectionPoolToken)
{
if (string.IsNullOrEmpty(machineName)) machineName = Environment.MachineName;
SqlConnectionStringBuilder cnbdlr;
try
{
cnbdlr = new SqlConnectionStringBuilder(connectionString);
}
catch
{
throw new ArgumentException("connection string was an invalid format");
}
cnbdlr.WorkstationID = machineName + connectionPoolToken;
return cnbdlr.ConnectionString;
}
public void Work()
{
int i = 0;
while (this.IsEnabled)
{
this.IsRunning = true;
try
{
Log log = new Log();
log.WorkItemId = Guid.NewGuid();
log.StartTime = DateTime.Now;
List<object> lst = new List<object>();
using (SqlConnection cn = new SqlConnection(this.dataCnString))
{
try
{
cn.Open();
using (SqlCommand cmd = new SqlCommand("Analysis.spSelectTestData", cn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) // DBHelper.ExecuteReader(cn, cmd))
{
while (dr.Read())
{
CreateClaimHeader2(dr, lst);
}
dr.Close();
}
cmd.Cancel();
}
}
catch { }
finally
{
cn.Close();
}
}
log.StopTime = DateTime.Now;
log.RouteName = this.Name;
log.HostName = this.machineName;
this.Logs.Add(log);
i++;
if (i > 1000)
{
Console.WriteLine(string.Format("Thread: {0} executed {1} items.", this.Name, i));
i = 0;
}
if (this.Sleep > 0) Thread.Sleep(this.Sleep);
}
catch { }
}
this.LogMessages();
this.IsRunning = false;
}
private void CreateClaimHeader2(IDataReader reader, List<object> lst)
{
lst.Add(reader["ClaimHeaderID"]);
lst.Add(reader["ClientCode"]);
lst.Add(reader["MemberID"]);
lst.Add(reader["ProviderID"]);
lst.Add(reader["ClaimNumber"]);
lst.Add(reader["PatientAcctNumber"]);
lst.Add(reader["Source"]);
lst.Add(reader["SourceID"]);
lst.Add(reader["TotalPayAmount"]);
lst.Add(reader["TotalBillAmount"]);
lst.Add(reader["FirstDateOfService"]);
lst.Add(reader["LastDateOfService"]);
lst.Add(reader["MaxStartDateOfService"]);
lst.Add(reader["MaxValidStartDateOfService"]);
lst.Add(reader["LastUpdated"]);
lst.Add(reader["UpdatedBy"]);
}
/// <summary>
/// Toes the data table.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data">The data.</param>
/// <returns></returns>
public DataTable ToDataTable<T>(IEnumerable<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
if (props == null) throw new ArgumentNullException("Table properties.");
if (data == null) throw new ArgumentNullException("data");
DataTable table = new DataTable();
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item) ?? DBNull.Value;
}
table.Rows.Add(values);
}
return table;
}
private void LogMessages()
{
using (SqlConnection cn = new SqlConnection(this.logCnString))
{
try
{
cn.Open();
DataTable dt = ToDataTable(this.Logs);
Console.WriteLine(string.Format("Logging {0} records for Thread: {1}", this.Logs.Count, this.Name));
using (SqlCommand cmd = new SqlCommand("Analysis.spInsertWorkItemRouteLog", cn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#dt", dt);
cmd.ExecuteNonQuery();
}
Console.WriteLine(string.Format("Logged {0} records for Thread: {1}", this.Logs.Count, this.Name));
}
finally
{
cn.Close();
}
}
}
}
}
1.A DataReader works in a connected environment,
whereas DataSet works in a disconnected environment.
2.A DataSet represents an in-memory cache of data consisting of any number of inter related DataTable objects. A DataTable object represents a tabular block of in-memory data.
SqlDataAdapter or sqlDataReader
Difference between SqlDataAdapter or sqlDataReader ?
Ans : 1.A DataReader works in a connected environment,
whereas DataSet works in a disconnected environment.
2.A DataSet represents an in-memory cache of data consisting of any number of inter related DataTable objects. A DataTable object represents a tabular block of in-memory data.
SqlDataAdapter or sqlDataReader