How to pass object to process output event - wpf

I have an ObservableCollection<Conversion> Queue, bound to ListBox control with ItemTemplate containing a TextBlock and a Button. When the button is clicked, a Win32 process starts. This process has an ErrorDataReceived event handler method which reads the process output and is supposed to update the PercentComplete property of the Conversion object in the collection. PercentComplete is bound to TextBlock's Text property.
How do I update PercentComplete from Win32 process event? I was hoping to pass the Conversion object to the ErrorDataReceived event handler, but the DataReceivedEventArgs only has a single Data property of type string.
Here is the code:
XAML:
<ListBox ItemsSource="{Binding Queue}" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding PercentComplete}" />
<Button Command="convertor:Commands.RunConversion">Run</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code-behind:
private ObservableCollection<Conversion> _queue;
public ObservableCollection<Conversion> Queue
{
get { return _queue; }
set
{
_queue = value;
RaisePropertyChange("Queue");
}
}
private Conversion _selectedItem;
public Conversion SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
RaisePropertyChange("SelectedItem");
}
}
private void RunConversion_Executed(object sender, ExecutedRoutedEventArgs e)
{
...
using (var ffmpeg = new Process())
{
...
ffmpeg.EnableRaisingEvents = true;
ffmpeg.ErrorDataReceived += FfmpegProcess_ErrorDataReceived;
// I realize it is weird I am working with ErrorDataReceived instead
// of OutputDataReceived event, but that's how ffmpeg.exe rolls.
ffmpeg.Start();
ffmpeg.BeginErrorReadLine();
}
}
private void FfmpegProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
var processOutput = e.Data;
var percentComplete = ParsePercentComplete(processOutput);
//TODO Pass percentComplete to Conversion.PercentComplete!?
}
Class:
public class Conversion : INotifyPropertyChanged
{
private double _percentComplete;
public double PercentComplete
{
get { return _percentComplete; }
set
{
_percentComplete = value;
RaisePropertyChange("PercentComplete");
}
}
public void RaisePropertyChange(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}

Ok, I solved it. The key to the solution was the process.Id which provides the reference to the process specific to the ObservableCollection item.
Specifically, I expanded the Conversion with Process Process property to store the information of that particular process, and then I can find the item in the collection and update its properties from process output in process' event handler.
Here is the updated code:
Code-behind:
private ObservableCollection<Conversion> _queue;
public ObservableCollection<Conversion> Queue
{
get { return _queue; }
set
{
_queue = value;
RaisePropertyChange("Queue");
}
}
private Conversion _selectedItem;
public Conversion SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
RaisePropertyChange("SelectedItem");
}
}
private void RunConversion_Executed(object sender, ExecutedRoutedEventArgs e)
{
...
var ffmpeg = new Process();
ffmpeg.EnableRaisingEvents = true;
ffmpeg.ErrorDataReceived += FfmpegProcess_ErrorDataReceived;
ffmpeg.Start();
conversion.Process = ffmpeg; // This is new
ffmpeg.BeginErrorReadLine();
}
private void FfmpegProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
var processOutput = e.Data;
var percentComplete = ParsePercentComplete(processOutput);
var processId = (sender as Process).Id; // These three lines are new
var conversion = Queue.Where(c => c.Process.Id == processId).FirstOrDefault();
conversion.PercentComplete = percentComplete; // WTF!!!!
}
Class
public class Conversion : INotifyPropertyChanged
{
private double _percentComplete;
public double PercentComplete
{
get { return _percentComplete; }
set
{
_percentComplete = value;
RaisePropertyChange("PercentComplete");
}
}
// New property
private Process _process;
public Process Process
{
get { return _process; }
set
{
_process= value;
RaisePropertyChange("Process");
}
}
public void RaisePropertyChange(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}

Related

wpf trigger dependency property refresh manually

I have a custom UserControl subclassing from RichTextBox. This class has a dependency property, Equation, that is bound two-way.
When the user drops an item onto the control I change Equation. This properly propagates the change to the other end of the binding, which triggers a property changed notification, but the UI is not changing. If I change the binding to a different object and back it then displays the updated Equation.
How can I force the refresh without changing the binding? Right now I'm setting Equation=null and then back which works, but that seems hackish. There must be something more elegant.
Here are relevant portions of the control. What I would like to happen is for the OnEquationChanged callback to be called after I change Equation (Equation.Components.Add(txt)).
public class EquationTextBox : RichTextBox
{
protected override void OnDrop(DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.StringFormat))
{
string str = (string)e.Data.GetData(DataFormats.StringFormat);
EquationText txt = new EquationText(str);
//// Preferred /////
Equation.Components.Add(txt);
//// HACK /////
Equation eqn = this.Equation;
eqn.Components.Add(txt);
this.Equation = null;
this.Equation = eqn;
///////////////
Console.WriteLine("Dropping " + str);
}
}
public Equation Equation
{
get { return (Equation)GetValue(EquationProperty); }
set { SetValue(EquationProperty, value); }
}
private static void onEquationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
string prop = e.Property.ToString();
EquationTextBox txtBox = d as EquationTextBox;
if(txtBox == null || txtBox.Equation == null)
return;
FlowDocument doc = txtBox.Document;
doc.Blocks.Clear();
doc.Blocks.Add(new Paragraph(new Run(txtBox.Equation.ToString())));
}
public static readonly DependencyProperty EquationProperty =
DependencyProperty.Register("Equation",
typeof(Equation),
typeof(EquationTextBox),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(onEquationChanged)));
private bool mIsTextChanged;
}
}
Here is the property on the other end of the two-way binding. The equation_PropertyChanged event is getting called in the above code as a result of Equation.Components.Add(txt);
public Equation Equation
{
get{ return mEquation; }
set { mEquation = value; NotifyPropertyChanged(); }
}
private void equation_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChanged("Equation");
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Edit --------------------------
Per the comments, I tried using a dispatcher like this (note that this is my first attempt at using a dispatcher)
string str = (string)e.Data.GetData(DataFormats.StringFormat);
EquationText txt = new EquationText(str);
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
Equation.Components.Add(txt);
NotifyPropertyChanged("Equation");
}));
but still no UI update.
Edit 2 --------------------------
The 2-way binding is done in XAML
<l:EquationTextBox x:Name="ui_txtVariableEquation" Grid.Row="0" Grid.Column="2"
Grid.RowSpan="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
AllowDrop="True"
Equation="{Binding SelectedVariableVM.Variable.Equation, Mode=TwoWay}">
</l:EquationTextBox>
Info relevant to the Components object (with in the Equation class)
public class Equation : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Equation()
{
mComponents = new ObservableCollection<EquationComponent>();
mComponents.CollectionChanged += new NotifyCollectionChangedEventHandler(components_CollectionChanged);
}
public Equation(string eqn) : this()
{
mComponents.Add(new EquationText(eqn));
}
public ObservableCollection<EquationComponent> Components
{
get{ return mComponents; }
set{ mComponents = value; NotifyPropertyChanged();}
}
public override string ToString()
{
string str = "";
for(int i=0; i<mComponents.Count; i++)
str += mComponents[i].ToString();
return str;
}
private void components_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Components");
}
private ObservableCollection<EquationComponent> mComponents;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Variable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Variable(string name = "var", VariableType type = VariableType.UnknownType) :
this(name, "", 0, type)
{
}
and ...
public class Variable : INotifyPropertyChanged
{
public Variable(string name, string unit, object val, VariableType type)
{
mEquation = new Equation(name + " = " + val.ToString() +
mEquation.PropertyChanged += new PropertyChangedEventHandler(equation_PropertyChanged);
}
...
public Equation Equation
{
get{ return mEquation; }
set { mEquation = value; NotifyPropertyChanged(); }
}
private void equation_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChanged("Equation");
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private Equation mEquation;
...
}
Variable.equation_PropertyChanged is called when the event is raised inside of the Equation class
I think the problem is that the value produced by the binding is not actually changing (it's still the same Equation object). If the DP value doesn't change, then your DP change handler will not be called.
Perhaps, in your DP change handler, you should subscribe to the new equation's PropertyChanged event and then rebuild your document when an underlying property changes:
private static void onEquationChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var txtBox = d as EquationTextBox;
if (txtBox == null)
return;
var oldEquation = e.OldValue as Equation;
if (oldEquation != null)
oldEquation.PropertyChanged -= txtBox.OnEquationPropertyChanged;
var newEquation = e.NewValue as Equation;
if (newEquation != null)
newEquation.PropertyChanged += txtBox.OnEquationPropertyChanged;
txtBox.RebuildDocument();
}
private void OnEquationPropertyChanged(object sender, EventArgs e)
{
RebuildDocument();
}
private void RebuildDocument()
{
FlowDocument doc = this.Document;
doc.Blocks.Clear();
var equation = this.Equation;
if (equation != null)
doc.Blocks.Add(new Paragraph(new Run(equation.ToString())));
}

Show Splash Screen & Progress Bar with Percentage using MVVM & WPF

I need to show Splash Screen with Image & progress bar.
In my application start up i have the code as below to show the main window.
SplashScreenWindowViewModel vm = new SplashScreenWindowViewModel();
AutoResetEvent ev = new AutoResetEvent(false);
Thread uiThread = new Thread(() =>
{
vm.Dispatcher = Dispatcher.CurrentDispatcher;
ev.Set();
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate()
{
SplashScreenWindow splashScreenWindow = new SplashScreenWindow();
splashScreenWindow = new SplashScreenWindow();
splashScreenWindow.Show();
splashScreenWindow.DataContext = vm;
vm.InstigateWorkCommand.Execute(null);
});
Dispatcher.Run();
});
uiThread.SetApartmentState(ApartmentState.STA);
uiThread.IsBackground = true;
uiThread.Start();
ev.WaitOne();
In my main viewmodel i have code as below
class MainviewModel : viewmodelbase
{
rivate string _message;
private object content;
private readonly BackgroundWorker worker;
private readonly ICommand instigateWorkCommand;
public SplashScreenWindowViewModel()
{
this.instigateWorkCommand = new
RelayCommand(() => this.worker.RunWorkerAsync(), () => !this.worker.IsBusy);
this.worker = new BackgroundWorker { WorkerReportsProgress = true };
this.worker.DoWork += this.DoWork;
this.worker.ProgressChanged += this.ProgressChanged;
_message = "0 % completed";
}
public ICommand InstigateWorkCommand
{
get { return this.instigateWorkCommand; }
}
private double _currentProgress;
public double CurrentProgress
{
get { return this._currentProgress; }
set
{
if (this._currentProgress != value)
{
this._currentProgress = value;
RaisePropertyChanged("CurrentProgress");
}
}
}
private int _progressMax;
public int ProgressMax
{
get { return this._progressMax; }
set
{
if(this._progressMax != value)
{
this._progressMax = value;
RaisePropertyChanged("ProgressMax");
}
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.CurrentProgress = e.ProgressPercentage;
}
private void DoWork(object sender, DoWorkEventArgs e)
{
// calling my long running operation
DAL.dotimeconsumingcode();
worker.ReportProgress((int)e.argument);
}
public string Message
{
get
{
return _message;
}
set
{
if (Message == value) return;
_message = value;
RaisePropertyChanged("Message");
}
}
public object Content
{
get
{
return content;
}
set
{
if (Content == value) return;
content = value;
RaisePropertyChanged("Content");
}
}
public Dispatcher Dispatcher
{
get;
set;
}
}
MY UI has one user control with progress bar and one splash main window.
when my long running operation is completed , my Main window(main application) is opened.
//User Control
<ProgressBar Height="27" Value="{Binding CurrentProgress, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Margin="53,162,57,0" Name="progressBar" Grid.Row="1"
Maximum="{Binding Path=ProgressMax, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding ProgressVisibility}" />
//SplashWindow
<localView:usercontrol/>
My Problem is
ProgressChangedevent is not firing and % completion is not showing up in the text block either. Please help
You have not registered a complete handler and you are not calling progress properly.
This sample from MSDN covers it all.
BackGroundWorker

Working with DelegateCommand's CanExecute action

I've a ViewModel class like this in a Prism / WPF project.
public class ContentViewModel : ViewModelBase, IContentViewModel
{
public ContentViewModel(IPersonService personService)
{
Person = personService.GetPerson();
SaveCommand = new DelegateCommand(Save, CanSave);
}
public Person Person { get; set; }
public DelegateCommand SaveCommand { get; set; }
private void Save()
{
// Save actions here...
}
private bool CanSave()
{
return Person.Error == null;
}
}
The person type used in the above ViewModel is defined as follows:
public class Person : INotifyPropertyChanged, IDataErrorInfo
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
// other properties are implemented in the same way as above...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _error;
public string Error
{
get
{
return _error;
}
}
public string this[string columnName]
{
get
{
_error = null;
switch (columnName)
{
// logic here to validate columns...
}
return _error;
}
}
}
An instance of ContentViewModel is set as DataContext of a View. Inside the View I've used binding to Person as follows:
<TextBox Text="{Binding Person.FirstName, ValidatesOnDataErrors=True}" />
<Button Content="Save" Command="{Binding SaveCommand}" />
When I make changes to TextBox which is binded to Person's properties like FirstName and click Save I could see the changes in ViewModel command handler. But if any of these properties fail in validation CanSave is never executed and button never gets disabled.
How do I disable a button based on DelegateCommand's CanExecute action handler in the above scenario?
In the constructor of ContentViewModel add this line
public ContentViewModel(IPersonService personService)
{
//GetPerson
Person.PropertyChanged +=person_PropertyChanged;
}
And write an method to handle that event in which you call either CommandManager.InvalidateRequerySuggested() or SaveCommand.RaiseCanExecuteChanged()
private void person_PropertyChanged(object sender, EventArgs args)
{
CommandManager.InvalidateRequerySuggested();
//SaveCommand.RaiseCanExecuteChanged()
}
Hope this works. :-)
try this with all the properties that can change error:
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("Error");
}
}
Alternatively
switch (columnName)
{
// logic here to validate columns...
OnPropertyChanged("Error");
}
The problem you are having is that the OnPropertyChanged is not being called when the error changes.
The next step is to subscribe to the person's propertychanged event when its created, and create a handler that checks for the propertychanged and then changes the boolean variable that the command uses.
public ContentViewModel(IPersonService personService)
{
Person = personService.GetPerson();
Person.PropertyChanged+= PersonPropertyChangedHandler;
SaveCommand = new DelegateCommand(Save, personHasError);
}
bool personHasError = false;
void PersonPropertyChangedHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Error")
{
if(Person.Error == null)
personHasError = true;
else
personHasError = false;
}
}
Hope this works. I built this by hand and didn't check it so let me know if its buggy or whatever and ill correct it
In the nutshell - you should call yourDelegateCommand.RaiseCanExecuteChanged() when you think that your CanExecute() return value can be changed.
In your example, you should notify through INotifyPropertyChanged interface that your Person.Error property is changed, subscribes to Person.PropertyChanged event in your ContentViewModel class and call SaveCommand.RaiseCanExecuteChanged() each time when your Person.Error is changed. Please be careful - in your scenario Person.Error isn't recalculated automatically when, for example, Person.FirstName is changed - you should do this manually.
UPDATED:
public class ContentViewModel : ViewModelBase, IContentViewModel
{
public ContentViewModel(IPersonService personService)
{
Person = personService.GetPerson();
Person.PropertyChanged += Person_PropertyChanged;
SaveCommand = new DelegateCommand(Save, CanSave);
}
private void PersonPropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
SaveCommand.RaiseCanExecuteChanged();
}
private void Save()
{
// Save actions here...
}
private bool CanSave()
{
return IsErrorPresented(Person);
}
private bool IsErrorPresented(object o)
{
if (!(o is IDataErrorInfo))
return false;
var propNames = o.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(p => p.Name);
var o2 = (o as IDataErrorInfo);
var errors = propNames.Select(p => o2[p])
.Where(p => !String.IsNullOrEmpty(p))
.ToList();
ValidationSummary.ErrorMessages = errors;
return errors.Count > 0;
}
}
<TextBox Text="{Binding Person.FirstName,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}" />
<Button Content="Save" Command="{Binding SaveCommand}" />
If you will also specify PropertyChanged as UpdateSourceTrigger, your save button will be updated during your typing..

Silverlight Combobox - Setting the SelectedItem MVVM

I have a ViewModel that sets the value for the "UserStructure" property. The problem is that the combobox wont bind to the value.
public class OwnerOccupierAccountViewModel : ViewModelBase
{
/// <summary>
/// Load combobox structures
/// </summary>
private readonly LoadOperation<Structure> _loadStructures;
private readonly LoadOperation<UnitOccupierDetail> _loadUnitOccupierDetails;
//public ICommand SaveAccountSettingsCommand { get; set; }
#region Properties
private ObservableCollection<Structure> _structures;
public ObservableCollection<Structure> Structures
{
get { return _structures; }
set
{
_structures = value;
RaisePropertyChanged("Structures");
}
}
private Structure _userStructure;
public Structure UserStructure
{
get { return _userStructure; }
set
{
_userStructure = value;
RaisePropertyChanged("SelectedStructure");
}
}
private UnitOccupierDetail _unitOccupierDetail;
public UnitOccupierDetail UnitOccupierDetail
{
get { return _unitOccupierDetail; }
set
{
_unitOccupierDetail = value;
RaisePropertyChanged("UnitOccupierDetail");
}
}
#endregion
public OwnerOccupierAccountViewModel()
{
// SaveAccountSettingsCommand = new DelegateCommand(SaveAccountSettings, CanSave);
UserAccountContext _userAccountContext;
if (!DesignerProperties.IsInDesignTool)
{
var loggedInUser = new Guid(WebContext.Current.User.UserID.ToString());
_userAccountContext = new UserAccountContext();
#region load structures
_loadStructures = _userAccountContext.Load(_userAccountContext.GetStructuresQuery());
_loadStructures.Completed += _loadStructuresCompleted;
#endregion
#region load user data
_loadUnitOccupierDetails =
_userAccountContext.Load(
_userAccountContext.GetUnitOccupierDetailsQuery().Where(
u => u.UserIDFK == loggedInUser && u.StructureFK == 92));
_loadUnitOccupierDetails.Completed += _loadUnitOccupierDetails_Completed;
#endregion
}
}
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
_unitOccupierDetail= new UnitOccupierDetail();
_unitOccupierDetail = _loadUnitOccupierDetails.Entities.First();
_userStructure = _unitOccupierDetail.Structure;
}
void _loadStructuresCompleted(object sender, EventArgs e)
{
var theseStructures = new ObservableCollection<Structure>(_loadStructures.Entities);
Structures = theseStructures;
}
//private void SaveAccountSettings(object param)
//{
//}
//private static bool CanSave(object param)
//{
// return true;
//}
}
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
DisplayMemberPath='StructureName'
SelectedValuePath='IDStructure'
SelectedItem='{Binding SelectedStructure,Mode=TwoWay}'
Width="200"
Height="25">
in xaml UserStructure instead SelectedStructure.
In your XAML SelectedItem is bound to SelectedStructure instead of UserStructure what you want
UPDATE:
Your code doesn't work because you should set to SelectedItem object that has reference equality with some object in ItemsSource. In your ViewModel you set Structures as result of one service operation and UserStructure as result of the other. UserStructure and some object in Structures can be equals (object.Equals) but not reference equals (object.ReferenceEquals). ComboBox like other ItemsControls doesn't compare items by equality, it compares they by identity. So, to have right code you should select from Structures object that equals to UserStructure and set it as UserStructure:
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
...
Structure userStructure = Structures.FirstOrDefault(s=>s.Equals(_unitOccupierDetail.Structure));
UserStructure = userStructure;
}
In this case you should be sure that Structures comes before. You can look at Reactive Extensions Observable.ForkJoin() method to syncronize 2 async calls.
Try such changes
public class OwnerOccupierAccountViewModel : ViewModelBase
{
/// <summary>
/// Load combobox structures
/// </summary>
private readonly LoadOperation<Structure> _loadStructures;
private readonly LoadOperation<UnitOccupierDetail> _loadUnitOccupierDetails;
//public ICommand SaveAccountSettingsCommand { get; set; }
#region Properties
private ObservableCollection<Structure> _structures;
public ObservableCollection<Structure> Structures
{
get { return _structures; }
set
{
_structures = value;
RaisePropertyChanged("Structures");
}
}
private Structure _userStructure;
public Structure UserStructure
{
get { return _userStructure; }
set
{
_userStructure = value;
RaisePropertyChanged("UserStructure");
}
}
private UnitOccupierDetail _unitOccupierDetail;
public UnitOccupierDetail UnitOccupierDetail
{
get { return _unitOccupierDetail; }
set
{
_unitOccupierDetail = value;
RaisePropertyChanged("UnitOccupierDetail");
}
}
#endregion
public OwnerOccupierAccountViewModel()
{
// SaveAccountSettingsCommand = new DelegateCommand(SaveAccountSettings, CanSave);
UserAccountContext _userAccountContext;
if (!DesignerProperties.IsInDesignTool)
{
var loggedInUser = new Guid(WebContext.Current.User.UserID.ToString());
_userAccountContext = new UserAccountContext();
#region load structures
_loadStructures = _userAccountContext.Load(_userAccountContext.GetStructuresQuery());
_loadStructures.Completed += _loadStructuresCompleted;
#endregion
#region load user data
_loadUnitOccupierDetails =
_userAccountContext.Load(
_userAccountContext.GetUnitOccupierDetailsQuery().Where(
u => u.UserIDFK == loggedInUser && u.StructureFK == 92));
_loadUnitOccupierDetails.Completed += _loadUnitOccupierDetails_Completed;
#endregion
}
}
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
_unitOccupierDetail= new UnitOccupierDetail();
_unitOccupierDetail = _loadUnitOccupierDetails.Entities.First();
_userStructure = _unitOccupierDetail.Structure;
}
void _loadStructuresCompleted(object sender, EventArgs e)
{
var theseStructures = new ObservableCollection<Structure>(_loadStructures.Entities);
Structures = theseStructures;
}
//private void SaveAccountSettings(object param)
//{
//}
//private static bool CanSave(object param)
//{
// return true;
//}
}
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
DisplayMemberPath='StructureName'
SelectedValuePath='IDStructure'
SelectedItem='{Binding UserStructure,Mode=TwoWay}'
Width="200"
Height="25">
Updated:
Hmm, maybe try changes here:
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
SelectedItem='{Binding UserStructure, Mode=TwoWay}'
Width="200"
Height="25">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StructureName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

WPF Chart Series Dynamic Databinding

How do I go about binding a chart series to a datasource where the data changes? I will of course need the chart series to reflect the changes to the underlying data. I need a fresh angle.
I think you mean charts from WPF Toolkit. Use ObservableCollection for add/remove events and INotifyPropertyChanged for update events.
Xaml:
<chart:Chart>
<chart:Chart.Series>
<chart:LineSeries DependentValuePath="Value" IndependentValuePath="Time"
Title="Example" x:Name="lineChart">
<chart:LineSeries.DependentRangeAxis>
<chart:LinearAxis Orientation="Y" Minimum="0" Maximum="20" />
</chart:LineSeries.DependentRangeAxis>
</chart:LineSeries>
</chart:Chart.Series>
</chart:Chart>
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var r = new Random();
var Items = new ObservableCollection<ChartItem>(
Enumerable.Range(0,5).Select(i=>new ChartItem(i,1)).ToList());
var t = new System.Timers.Timer { Interval = 1000 };
var start = DateTime.Now;
//occurs every 1 second
t.Elapsed += (s, e) => Dispatcher.Invoke((Action)(() =>
{
Items[r.Next(0, Items.Count)].Value = r.Next(20);
Items.Add(new ChartItem((e.SignalTime - start).TotalSeconds, r.Next(20)));
}));
t.Start();
lineChart.ItemsSource = Items;
}
}
Item class:
public class ChartItem:INotifyPropertyChanged
{
public ChartItem(double t, double v)
{
time = t;
myVar = v;
}
private double time;
public double Time
{
get { return time; }
set
{
time = value;
OnPropertyChanged("Time");
}
}
private double myVar;
public double Value
{
get { return myVar; }
set
{
myVar = value;
OnPropertyChanged("Value");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
  
Also, dynamic charts: http://dynamicdatadisplay.codeplex.com/

Resources