I have a method/procedure which works well, however it takes ages to do its thing so I want to move it into a background worker so people can still use the app.
Here is the code. (I cut down as much as I could)
public partial class NetworkInformation : UserControl, INotifyPropertyChanged
{
public NetworkInformation()
{
InitializeComponent();
Discovery();
}
public void Discovery()
{
GetIcon Icon = new GetIcon();
BitmapImage IconOfComputer = null;
List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
BitmapImage On = Icon.LoadIcon(#"/Images/Icons/ComputerOn.ico");
BitmapImage Off = Icon.LoadIcon(#"/Images/Icons/ComputerOff.ico");
foreach (DirectoryEntry Node in Discover.Children)
{
try
{
if (Node.Properties.Count > 0)
{
IconOfComputer = On;
}
}
catch
{
IconOfComputer = Off;
}
if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
}
ListView_LocalComputers.ItemsSource = NetworkedComputers;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public class DiscoveredComputer : INotifyPropertyChanged
{
private string _ComputerName;
public string ComputerName
{
get { return _ComputerName; }
set
{
_ComputerName = value;
this.NotifyPropertyChanged("ComputerName");
}
}
private BitmapImage _Image;
public BitmapImage Image {
get { return _Image; }
set
{
_Image = value;
this.NotifyPropertyChanged("Image");
}
}
private String _MyToolTip;
public String MyToolTip
{
get { return _MyToolTip; }
set
{
_MyToolTip = value;
this.NotifyPropertyChanged("ToolTip");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public class GetIcon
{
public BitmapImage IconStorage { get; set; }
public BitmapImage LoadIcon(String IconPath)
{
BitmapImage GeneratedIcon = new BitmapImage();
GeneratedIcon.BeginInit();
GeneratedIcon.UriSource = new Uri("pack://application:,,," + IconPath, UriKind.RelativeOrAbsolute);
GeneratedIcon.EndInit();
IconStorage = GeneratedIcon;
return GeneratedIcon;
}
}
}
This all works awesomely, somehow...
Here is the code I:developed for my background worker
public partial class MyBackgroundWorker : UserControl
{
WorkerData BGW;
public MyBackgroundWorker()
{
InitializeComponent();
BGW = new WorkerData();
#region Workers Events
BGW.ThisWorker.DoWork += new DoWorkEventHandler(Workers_DoWork);
BGW.ThisWorker.ProgressChanged += new ProgressChangedEventHandler(Workers_Progress);
BGW.ThisWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Workers_Completed);
BGW.ThisWorker.WorkerReportsProgress = true;
BGW.ThisWorker.WorkerSupportsCancellation = true;
#endregion
}
public void RibbonButton_EventClickStart(object sender, RoutedEventArgs e)
{
BGW.ThisWorker.RunWorkerAsync();
}
public void UserForm_Loaded(object sender, RoutedEventArgs e)
{
}
public void RibbonButton_EventClick(object sender, RoutedEventArgs e)
{
BGW.ThisWorker.CancelAsync();
}
public void Workers_DoWork(object sender, DoWorkEventArgs e)
{
}
public void Workers_Progress(object sender, ProgressChangedEventArgs e)
{
BGW.ThisWorkersProgress = e.ProgressPercentage;
}
public void Workers_Completed(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) { BGW.ThisWorkersResult = "Cancelled By User"; }
else if (e.Error != null) { BGW.ThisWorkersResult = "Error Encountered: " + e.Error.Message; }
else
{
BGW.ThisWorkersResult = "Task Completed Successfully";
BGW.WorkersReturnObject = e.Result;
}
}
}
public class WorkerData
{
public BackgroundWorker ThisWorker { get; set; }
public int ThisWorkersProgress { get; set; }
public string ThisWorkersResult { get; set; }
public object WorkersReturnObject { get; set; }
public object ThisWorkersJob { get; set; }
public WorkerData()
{
ThisWorker = new BackgroundWorker();
}
}
So how do I get my background worker to run the Discovery method I have created?
You need to do your work in the DoWork event handler.
I don't know if you need a whole separate class for this. I prefer to create these as I need them, on the fly. I think you'll get yourself shoehorned, where you'll use your class in multiple places and then decide you want to do something else in Workers_Completed in certain cases, or do something different when an error occurs in certain cases, and that one class could end up being a tangled-up pain. That's just my opinion though.
Also, you have to be very careful about touching the UI thread from your BackgroundWorker. In the example below, I'm passing in your node count to the DoWork event, instead of having it possibly touch a UI component directly. I'm also passing the list to the RunWorkerCompleted event, so that you're back in the main thread when it tries to attach the list to your ListView.
var bw = new BackgroundWorker();
bw.DoWork += (s, e) =>
{
var nodePropertiesCount = (int)e.Argument;
// the guts of `Discovery` go in here
e.Result = NetworkedComputers;
};
bw.RunWorkerCompleted += (s, e) =>
{
if (e.Error != null)
{
// Task Completed Successfully
ListView_LocalComputers = (List<DiscoveredComputer>)e.Result;
}
else
{
// Error Encountered
}
};
bw.RunWorkerAsync(Node.Properties.Count);
SLaks answer is correct, but you apparently don't understand what that means. I'd suggest taking the guts of Discover() and putting them in the Workers_DoWork() method like this:
public void Workers_DoWork(object sender, DoWorkEventArgs e)
{
var backgroundWorker = sender as BackgroundWorker;
GetIcon Icon = new GetIcon();
BitmapImage IconOfComputer = null;
List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
BitmapImage On = Icon.LoadIcon(#"/Images/Icons/ComputerOn.ico");
BitmapImage Off = Icon.LoadIcon(#"/Images/Icons/ComputerOff.ico");
while (!backgroundWorker.CancellationPending)
{
foreach (DirectoryEntry Node in Discover.Children)
{
try
{
if (Node.Properties.Count > 0)
{
IconOfComputer = On;
}
}
catch
{
IconOfComputer = Off;
}
if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
}
break;
}
if(backgroundWorker.CancellationPending)
{
e.Cancel = true;
}
else
{
e.Result = NetworkedComputers;
}
}
And then modifying your Workers_Completed() like this:
public void Workers_Completed(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) { BGW.ThisWorkersResult = "Cancelled By User"; }
else if (e.Error != null) { BGW.ThisWorkersResult = "Error Encountered: " + e.Error.Message; }
else
{
BGW.ThisWorkersResult = "Task Completed Successfully";
//BGW.WorkersReturnObject = e.Result;
//background worker can't touch UI components
ListView_LocalComputers.ItemsSource = e.Result as List<DiscoveredComputer>;
}
}
I suggest these changes, or something similar, because the background worker can't modify/access UI components (like your ListView), so it has to pass back the value to use for the ListView view its Result property. I also included a simple way of detecting cancellation; I'll leave progress reporting up to you to implement.
Related
I am developing and wpf app and in which I need to update data on basis of click on button. I tried to update in code behind but it did not work so I used datacontext but still no use. I saw various solutions and have used mode=TwoWay, UpdateSourceTrigger but it does not work.
<Grid DataContext="{Binding Dashboard, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
//Content
</Grid>
In cs file
public ViewModels.DashboardVM _DashVM = new ViewModels.DashboardVM();
async private void DashboardPage_Loaded(object sender, RoutedEventArgs e)
{
try
{
await _DashVM.GetDashboardData();
this.DataContext = _DashVM;
}
catch (Exception ex)
{
System.Diagnostics.Debug.Write(ex.Message);
}
}
and changing data context here
async private void StoresList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
var item = (sender as ListView).SelectedItem as Models.StoresLM;
if(item!=null)
{
Properties.Settings.Default.StoreId = item.id;
Properties.Settings.Default.Save();
await _DashVM.GetDashboardData();
this.DataContext = _DashVM;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Write(ex.Message);
}
}
my View Model is
public class DashboardVM : INotifyPropertyChanged
{
private Models.DashboardM _dashboard;
public Models.DashboardM Dashboard
{
get { return _dashboard; }
set { _dashboard = value; RaisePropertyChanged("Dashboard"); }
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public static event EventHandler<Boolean> IsLoading = delegate { };
async public Task<Boolean> GetDashboardData()
{
try
{
if (InternetTools.IsNetworkConnected())
{
IsLoading(this, true);
var storeId = Convert.ToString(Properties.Settings.Default.StoreId);
var Response = await new WebServiceUtility().PostRequest(string.Format(StringUtility.DashboardApi, Convert.ToString(Properties.Settings.Default.StoreId)), new[] { new KeyValuePair<string, string>("api_key", "dbjh") });
if (Response.IsSuccessStatusCode)
{
var DashboardData = await Response.Content.ReadAsStringAsync();
var jsonObject = Newtonsoft.Json.Linq.JObject.Parse(DashboardData);
if (Convert.ToString(jsonObject["success"]).IndexOf("True", StringComparison.OrdinalIgnoreCase) >= 0)
{
var DashObject = jsonObject.ToObject<Models.DashboardM>();
Properties.Settings.Default.Currency = DashObject.data.store.currency.StartsWith("&") ? System.Net.WebUtility.HtmlDecode(DashObject.data.store.currency) : System.Text.RegularExpressions.Regex.Unescape(DashObject.data.store.currency);
DashObject.data.store.currency = StringUtility.Currency;
Properties.Settings.Default.Save();
Dashboard = null;
Dashboard = DashObject;
}
}
}
else
NotificationUtility.ShowErrorMessage(NotificationUtility.MsgType.InternetError);
}
catch
{
}
IsLoading(this, false);
return true;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
Can anybody help?
I was able to resolve the problem by editing the code. It was not getting notified due to reassigning of object. I don't know the reason but I just changed this code
async private void StoresList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
var item = (sender as ListView).SelectedItem as Models.StoresLM;
if(item!=null)
{
Properties.Settings.Default.StoreId = item.id;
Properties.Settings.Default.Save();
await _DashVM.GetDashboardData();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Write(ex.Message);
}
}
Hope it helps somebody like me.
Trying to get clear about flaw in this code:
Scenario 1:
This scenario uses data binding and causes the very well known cross-thread exception in the NotifyPropertyChanged() method in the PriceSimulator class.
Scenario 2:
This scenario solves the problem by subscribing to the PropertyChanged event of PriceSimulator, eliminates the cross-thread issue but has to avoid data binding altogether.
Assuming Scenario 1 was the intended scenario and assuming one has no knowledge of the inner workings of PriceSimulator and just wanted to bind to the Price property, what is the core issue here?
Form1.cs:
public partial class Form1 : Form
{
PriceSimulator simul;
Action labelAction;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
labelAction = new Action(SetLabel);
simul = new PriceSimulator(5, 1000);
//Scenario 1:
//Use data binding and get Cross-Thread exception
//label1.DataBindings.Add("Text", simul, "Price");
//Scenario 2:
//This works fine
//Subscribe to PropertyChanged event
simul.PropertyChanged += task_PropertyChanged;
simul.Start();
}
//Scenario 2:
void task_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (label1.InvokeRequired)
Invoke(labelAction);
else SetLabel();
}
private void SetLabel()
{
label1.Text = simul.Price.ToString("C2");
}
}
PriceSimulator.cs:
public class PriceSimulator : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int max, delay, priceValue;
private Timer timer;
public PriceSimulator(int max, int delay)
{
this.max = max;
this.delay = delay;
}
public void Start()
{
timer = new Timer(CallbackProc, null, delay, delay);
}
private void CallbackProc(object obj)
{
if (++Price >= max)
timer.Dispose();
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
try
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
catch (Exception ex)
{
timer.Dispose();
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
public int Price
{
get
{
return priceValue;
}
set
{
if (priceValue != value)
{
priceValue = value;
NotifyPropertyChanged();
}
}
}
}
You have to have the current context in your PriceSimulator class:
private readonly SynchronizationContext _context = SynchronizationContext.Current;
Now that you have the context, you can use it to update the UI:
_context.Post(delegate
{
if (++Price >= max)
timer.Dispose();
}, null);
Problem
Problem only comes when I try to insert. However I could browse through records. When I insert a new record, the UserControl does not consider the value I typed in, instead it stores as null. I basically think on New record, it does not sync.
The Form Layout
Purpose of UserControl
The DB stores Field value in the form of JKB-932340094VN00 where I use my UserControl for split Values by - and display it in 2 TextBox in it. So one TextBox will have the Value of JKB and other will have 932340094VN00
UserControl is as follows:
public partial class ucClientAccountIDParser : UserControl, INotifyPropertyChanged
{
public ucClientAccountIDParser()
{
InitializeComponent();
id = "JKB-821230063VN00";
txtClientAccountID.TextChanged += new EventHandler(clientaccountIDChanged);
txtCustodyID.TextChanged += new EventHandler(custodyIDChanged);
}
private string _clientaccountID = string.Empty;
public string ClientaccountID
{
get { return _clientaccountID; }
set
{
_clientaccountID = value;
txtClientAccountID.Text = this._clientaccountID;
}
}
private string _custodyID = string.Empty;
public string CustodyID
{
get { return _custodyID; }
set
{
_custodyID = value;
txtCustodyID.Text = this._custodyID;
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public string id = string.Empty;
[System.ComponentModel.Bindable(true)]
public string Text
{
get
{
return id;
}
set
{
id = value;
if (id != null)
{
string[] aVal = id.Split('-');
if (aVal.Length > 1)
{
this.CustodyID = aVal[0];
this.ClientaccountID = aVal[1];
}
else
{
this.ClientaccountID = id;
}
}
else
{
this.CustodyID = string.Empty;
this.ClientaccountID = string.Empty;
}
NotifyPropertyChanged(id);
}
}
public void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
private void clientaccountIDChanged(object sender, EventArgs e)
{
this.id = string.Format("{0}-{1}", txtCustodyID.Text, (sender as TextBox).Text);
NotifyPropertyChanged(this.id);
}
private void custodyIDChanged(object sender, EventArgs e)
{
this.id = string.Format("{0}-{1}", (sender as TextBox).Text, txtClientAccountID.Text);
NotifyPropertyChanged(this.id);
}
}
This is how I bind it:
try
{
this.Init(null);
this.Text = "General Account Add/Change";
objTable = new ClientAccountBusinessTable();
this.PrimaryDataAdapters = new ClientAccountBusinessTable[1] { objTable };
this.PrimaryDataGridView = null;
objTable.KeyDBField = "CLIENTID";
PrimaryBindingSource = new BindingSource[1] { bindingSource1 };
PrimaryDataSet.Tables.Add(objTable.GetBusinessTable());
SetKeyDBControl(new Control[1] { ucClientAccountIDParser1 }, new string[1] { "CLIENTID" });
ucClientAccountIDParser1.DataBindings.Clear();
ucClientAccountIDParser1.DataBindings.Add(new Binding("Text", this.bindingSource1, "CLIENTID"));
bindingSource1.DataMember = this.PrimaryDataSet.Tables[0].TableName;
bindingSource1.DataSource = this.PrimaryDataSet;
}
catch (Exception ex)
{
objTable = null;
throw ex;
}
I cant figure out why it takes null. Let me know if I carried out design of UserControl in proper way.
I hope anybody can help me out.
I am trying to bind a list of strings to the contents of a list box. For some reason, I get results for bluetape list, but the contents of BluetapeList does not ever make it into the listbox. Any help would be much appreciated!
XAML:
<ListBox
Name="lbxTapeIn"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="1"
Width="70"
Height="80"
SelectionChanged="TapeSelectionChanged"
ItemsSource="{Binding}"
SelectedValue="{Binding SelectedBt}"
Background="DeepSkyBlue"
Foreground="MidnightBlue"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="5"/>
Code Behind:
public partial class OverrideAoiBinningWindow : Window
{
private OverrideAoiBinningWindowViewModel ovAoiBinWin;
public OverrideAoiBinningWindow()
{
InitializeComponent();
ovAoiBinWin = new OverrideAoiBinningWindowViewModel(tvwWaferList, txtFilter);
AssignDataContexts();
}
private void AssignDataContexts()
{
btnChooseWafer.DataContext = ovAoiBinWin;
btnSave.DataContext = ovAoiBinWin;
txtWafer.DataContext = ovAoiBinWin;
cbxAoiState.DataContext = ovAoiBinWin;
lbxTapeIn.DataContext = ovAoiBinWin.BluetapeList;
}
private void TapeSelectionChanged(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(ovAoiBinWin.SelectedWafer))
{
if (cbxAoiState.SelectedValue != null)
{
btnSave.IsEnabled = true;
}
}
}
private void AoiStateChanged(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(ovAoiBinWin.SelectedWafer))
{
if (lbxTapeIn.SelectedValue != null)
{
btnSave.IsEnabled = true;
}
}
}
private void Close(object sender, RoutedEventArgs e)
{
this.Close();
}
}
View Model:
public class OverrideAoiBinningWindowViewModel : ViewModelBase, ISelectWafers
{
public OverrideAoiBinningWindowViewModel(TreeView tvwWaferList, TextBox txtFilter)
{
// Set private fields
this.tvwWaferList = tvwWaferList;
this.txtFilter = txtFilter;
// Instantiate objects and initialize settings
this.InstantiateObjects();
this.SetControlSettings();
// Run the initialization thread
initThread.RunWorkerAsync();
}
public string SelectedWafer
{
get
{
return selectedWafer;
}
set
{
selectedWafer = value;
OnPropertyChanged("SelectedWafer");
}
}
public string SelectedBt
{
get
{
return selectedBt;
}
set
{
selectedBt = value;
OnPropertyChanged("SelectedBt");
}
}
public string SelectedAoiState
{
get
{
return selectedAoiState;
}
set
{
selectedAoiState = value;
OnPropertyChanged("SelectedAoiState");
}
}
public List<string> AOIStateList
{
get
{
return aoiStateList;
}
set
{
aoiStateList = value;
OnPropertyChanged("AOIStateList");
}
}
public List<string> BluetapeList
{
get
{
return bluetapeList;
}
set
{
bluetapeList = value;
OnPropertyChanged("BluetapeList");
}
}
public ICommand SelectWaferCommand
{
get
{
if (selectWaferCommand == null)
{
selectWaferCommand = new DelegateCommand(SelectWafer);
}
return selectWaferCommand;
}
}
public ICommand SaveAoiStateCommand
{
get
{
if (saveAoiStateCommand == null)
{
saveAoiStateCommand = new DelegateCommand(SaveAoiState);
}
return saveAoiStateCommand;
}
}
private void InstantiateObjects()
{
initThread = new BackgroundWorker();
aoiStateList = new List<string>();
bluetapeList = new List<string>();
converter = new WaferIDConverter();
}
private void SetControlSettings()
{
initThread.WorkerReportsProgress = false;
initThread.WorkerSupportsCancellation = false;
initThread.DoWork += InitThread_DoWork;
initThread.RunWorkerCompleted += InitThread_RunWorkerCompleted;
}
private void PopulateAoiStateList()
{
aoiStateList.Add("True");
aoiStateList.Add("False");
aoiStateList.Add("NotBinned");
aoiStateList.Add("NeverAOI");
}
private void PopulateBluetapeList()
{
waferQueries = new WaferQueries(
DataLibrary.GetSingulationOne(selectedWafer));
foreach (BlueTape tape in waferQueries.GetBlueTapeList())
{
bluetapeList.Add(tape.Name);
}
OnPropertyChanged("BluetapeList");
}
private void SaveAoiState()
{
Mouse.OverrideCursor = Cursors.Wait;
singOne = new SingOneTable();
singOne.OverrideAoiState(selectedWafer, selectedBt, selectedAoiState);
Mouse.OverrideCursor = null;
MessageBox.Show(
"The AOI state of " + selectedBt + " from " + selectedWafer +
" has been successfully changed to " + selectedAoiState + "!",
"AOI State Saved", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
public void SelectWafer()
{
Mouse.OverrideCursor = Cursors.Wait;
SelectedWafer = tvwWaferList.SelectedValue.ToString();
PopulateBluetapeList();
Mouse.OverrideCursor = null;
}
private void InitThread_DoWork(object sender, DoWorkEventArgs e)
{
if (!handled)
{
PopulateAoiStateList();
tvwPresenter = new TreeViewPresenter(tvwWaferList, txtFilter, this);
tvwPresenter.WaferList = DataLibrary.GetWaferList();
handled = true;
}
}
private void InitThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
tvwPresenter.TreeView.DataContext = tvwPresenter.ProcessesAndWafers;
tvwPresenter.WaferListCache = tvwPresenter.ProcessesAndWafers;
tvwPresenter.ProcessArray = tvwPresenter.WaferListCache.ToArray();
//TODO: Update Status text block
}
}
Answered (2nd comment) by Dilshod:
The fix was to change my List to ObservableCollection.
Once I made that change, everything worked as expected; thanks Dilshod!
Binding WPF Drop-Down control dynamically (C#).
I am using the following simple solution for binding WPF drop-down (combo-box) to a Dictionary object programmatically (C#). In this particular example Dictionary contains the list of Countries with corresponding 2-digit Country Codes (keys):
Listing 1. Dictionary object contains list of Countries w/2-digit Country codes
Dictionary<string, string> _co = new Dictionary<string, string>();
_co.Add(String.Empty, String.Empty);
_co.Add("US", "United States");
_co.Add("CA", "Canada");
_co.Add("MX", "Mexico");
Listing 2. Binding Drop-down to Dictionary object (WPF/C#)
// binding to country list Dictionary object (_co)
_cmbCountry.ItemsSource = _co;
// Country 2-digit Code used as a key
_cmbCountry.SelectedValuePath = _dKey;
// Country Name (string to display)
_cmbCountry.DisplayMemberPath = _dValue;
// first index selected
_cmbCountry.SelectedIndex = 0;
// DropDownClosed event subscription using Lambda notation
_cmbCountry.DropDownClosed += (s, e) => ComboBox_Closed(s, e);
The code snippet above (Listing 2.) also shows how to subscribe to the control event using 'short-cut' Lambda style. Hope this will help. Regards, AB
PS. You can also find more information on Drop-Down control binding techniques in my article: Binding DropDownList to various data structures in Microsoft ASP.NET
I have a class with INotifyPropertyChanged interface. There is a property with the name Total Progress.
I have a Form with Progress Bar on it. I want to send the TotalProgress property changed notifications to this Progress Bar and set it's value.
Do I need to catch the PropertyChangedEvent in the Form also?
Edit: WPF Form Code
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System;
using System.Windows.Threading;
namespace SUpdater
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
BackgroundWorker bw = new BackgroundWorker();
DownloadFile FileDownloadClass = new DownloadFile();
public MainWindow()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
progressBar1.SetBinding(System.Windows.Controls.ProgressBar.ValueProperty, new Binding("TotalPercentCompleted"));
progressBar1.DataContext = FileDownloadClass;
FileDownloadClass.PropertyChanged +=new PropertyChangedEventHandler(FileDownloadClass_PropertyChanged);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
FileDownloadClass.DownloadFiles();
if ((bw.CancellationPending == true))
e.Cancel = true;
else
{
bw.ReportProgress(FileDownloadClass.TotalPercentCompleted);
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.lblConnectionStatus.Content = " Download Canceled!";
}
else if (!(e.Error == null))
{
this.lblConnectionStatus.Content = ("Error: " + e.Error.Message);
}
else
{
this.lblConnectionStatus.Content = "Done!";
}
}
private void FileDownloadClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lblKbCompleted.Content = e.ProgressPercentage.ToString();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
bw.RunWorkerAsync();
}
}
}
Edit: DownloadFile Class Code
sealed class DownloadFile:INotifyPropertyChanged
{
#region Private Fields
// These fields hold the values for the public properties.
private int progressBarValue = 0;
private int totalKbCompleted = 0;
private int totalBytesReceived = 0;
private int remoteFileSize = 0;
private string fileName = String.Empty;
private string statusMessage = String.Empty;
#endregion
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#region Public Properties
public int TotalKbCompleted
{
get { return this.totalKbCompleted; }
set
{
if (value != this.totalKbCompleted)
{
this.totalKbCompleted = value/1024;
NotifyPropertyChanged("TotalKbCompleted");
}
}
}
public int TotalBytesReceived
{
get { return this.totalBytesReceived; }
set
{
if (value != this.totalBytesReceived)
{
this.totalBytesReceived = value;
NotifyPropertyChanged("TotalBytesReceived");
}
}
}
public int RemoteFileSize
{
get { return this.remoteFileSize; }
set
{
if (value != this.remoteFileSize)
{
this.remoteFileSize = value;
NotifyPropertyChanged("RemoteFileSize");
}
}
}
public string CurrentFileName
{
get { return this.fileName; }
set
{
if (value != this.fileName)
{
this.fileName = value;
NotifyPropertyChanged("CurrentFileName");
}
}
}
public string StatusMessage
{
get { return this.statusMessage; }
set
{
if (value != this.statusMessage)
{
this.statusMessage = value;
NotifyPropertyChanged("StatusMessage");
}
}
}
#endregion
public Int16 DownloadFiles()
{
try
{
statusMessage = "Attempting Connection with Server";
DoEvents();
// create a new ftpclient object with the host and port number to use
FtpClient ftp = new FtpClient("mySite", 21);
// registered an event hook for the transfer complete event so we get an update when the transfer is over
//ftp.TransferComplete += new EventHandler<TransferCompleteEventArgs>(ftp_TransferComplete);
// open a connection to the ftp server with a username and password
statusMessage = "Connected. Authenticating ....";
ftp.Open("User Name", "Password");
// Determine File Size of the compressed file to download
statusMessage = "Getting File Details";
RemoteFileSize = Convert.ToInt32(ftp.GetFileSize("myFile.exe"));
ftp.TransferProgress += new EventHandler<TransferProgressEventArgs>(ftp_TransferProgress);
statusMessage = "Download from Server";
ftp.GetFile("myFile.exe", "E:\\Test\\myFile.exe", FileAction.Create);
// close the ftp connection
ftp.Close();
statusMessage = "Download Complete";
return 1;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
return 0;
}
}
private void ftp_TransferProgress(object sender, TransferProgressEventArgs e)
{
totalBytesReceived = Convert.ToInt32(e.BytesTransferred.ToString());
totalKbCompleted = Convert.ToInt32(totalKbCompleted + Convert.ToInt32(totalBytesReceived));
progressBarValue = totalKbCompleted;
}
}
You can use control binding:
Windows Forms:
progressBar1.DataBindings.Add("Value", dataSource, dataMember, true,
DataSourceUpdateMode.OnPropertyChanged);
where the dataSource is your class. and the dataMember is the property name in that class "TotalProgress".
Edit: For WPF
progressBar1.SetBinding(ProgressBar.ValueProperty, new Binding("ProgressTotal"));
progressBar1.DataContext = the instance of the class you want to bind to its property;
For more information about wpf data binding check this and this.
Edit2: Here is an full example:
Foo _foo = new Foo();
DispatcherTimer _dispatcherTimer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
_dispatcherTimer.Interval = TimeSpan.FromSeconds(1);
_dispatcherTimer.Tick += _dispatcherTimer_Tick;
_dispatcherTimer.Start();
progressBar1.SetBinding(ProgressBar.ValueProperty, new Binding("ProgressTotal"));
progressBar1.DataContext = _foo;
}
private void _dispatcherTimer_Tick(object sender, EventArgs e)
{
_foo.ProgressTotal = (_foo.ProgressTotal + 10) % progressBar1.Maximum;
}
public class Foo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private double _progressTotal = 0;
public double ProgressTotal
{
get { return _progressTotal; }
set
{
if (value != _progressTotal)
{
_progressTotal = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ProgressTotal"));
}
}
}
}
}
Edit: Add a timer to preview the effect.
Edit: After you uploading your code, the problem appears in two positions:
The name of the variable is TotalKbCompleted no TotalPercentCompleted. so change the binding line to:
progressBar1.SetBinding(System.Windows.Controls.ProgressBar.ValueProperty, new Binding("TotalKbCompleted"));
You are updating the totalKbCompleted instead of TotalKbCompleted so the property changed will not trigger.