Binding a ListBox to a List<string> - c#

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

Related

WPF Set attributes back after a event is finished

so what i would like to is setting back some attributes after an Custom Event is finished.
Scenario i have a save BackupDrives Class that does collection of data and then offers a Event to be called after its done.
Changing object properties can be done by button click, what i would like is to set them back to the same value after the event is finished.
Button click does the thing :
private void bbdrives_Click(object sender, RoutedEventArgs e)
{
backup.SaveDrives += OnSavingDrives;
backup.DrivesSave();
Drive_text.Visibility = Visibility.Visible;
drives_progres.Visibility = Visibility.Visible;
drives_progres.IsIndeterminate = true;
}
Now the triggered event method is not able to change the properties back.
private void OnSavingDrives(object sender, DrivesEventArgs e)
{
Directory.CreateDirectory(....);
File.WriteAllLines(e.Something, e.List2ToSave);
File.WriteAllLines(e.Something_lse, e.List1ToSave);
Drive_text.Visibility = Visibility.Collapsed;
drives_progres.Visibility = Visibility.Collapsed;
drives_progres.IsIndeterminate = false;
}
How do i do this. Since this does not work.
And on other thig here to - when i run the GUI i need to click 2 times one the same button to start it all. Done Code Clean + Rebuild. Still the same.
---EDIT---
As for code here you go.
This is a Class for collecting method and event.
public class DrivesEventArgs : EventArgs
{
string MYDOC = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
const string backup_Drives = "....";
const string backup_Letters = "...";
public List<string> List1ToSave = new List<string>();
public List<string> List2ToSave = new List<string>();
public string SAVE_DRIVE_Letter
{
get
{
string name = Path.Combine(MYDOC, backup_Letters);
return name;
}
}
public string SAVE_DRIVE_Path
{
get
{
string name = Path.Combine(MYDOC, backup_Drives);
return name;
}
}
}
public class DrivesBackup
{
private const string path = "Network";
private List<string> drives_to_save = new List<string>();
private List<string> letters_for_drives = new List<string>();
private RegistryKey reg1, reg2;
public event EventHandler<DrivesEventArgs> SaveDrives;
public void DrivesSave()
{
var data = new DrivesEventArgs();
try
{
if (drives_to_save.Count == 0)
{
reg1 = Registry.CurrentUser.OpenSubKey(path);
string[] mounted_drives = reg1.GetSubKeyNames();
foreach (var drive in mounted_drives)
{ //get all UNC Paths from mounted_drives
string[] getUNC = { path, drive };
string fullUNC = Path.Combine(getUNC);
reg2 = Registry.CurrentUser.OpenSubKey(fullUNC);
string UNCPath = reg2.GetValue("RemotePath").ToString(); //getting UNC PATH
Console.WriteLine(UNCPath);
data.List1ToSave.Add(drive.ToString());
data.List2ToSave.Add(UNCPath);
OnSaveDrives(data);
}
}
}
catch (Exception er)
{
throw er;
}
}
protected virtual void OnSaveDrives(DrivesEventArgs eD)
{
SaveDrives?.Invoke(this, eD);
}
Now here is the MAINWINDOW WPF
public partial class MainWindow : Window
{
DrivesBackup backup = new DrivesBackup();
public MainWindow()
{
InitializeComponent();
}
private void bbdrives_Click(object sender, RoutedEventArgs e)
{
backup.DrivesSave();
backup.SaveDrives += OnSavingDrives;
Drive_text.Visibility = Visibility.Visible;
drives_progres.Visibility = Visibility.Visible;
drives_progres.IsIndeterminate = true;
}
private void OnSavingDrives(object sender, DrivesEventArgs e)
{
Directory.CreateDirectory(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), #"SEG-Backup"));
File.WriteAllLines(e.SAVE_DRIVE_Path, e.List2ToSave);
File.WriteAllLines(e.SAVE_DRIVE_Letter, e.List1ToSave);
Drive_text.Visibility = Visibility.Collapsed;
drives_progres.Visibility = Visibility.Collapsed;
drives_progres.IsIndeterminate = false;
}
}
Now i do hope this would make some things more clear.

UserControl Not Updating to Table

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.

Why List Box items are not update during Filtering?

I`m writing a program to view product Information in a Listbox. I have a text box for searching that automatically filters the list as you typing by ProductName. I have run through my C# code numerous times and I can see the filter actually working but I cannot visually get it to filter or 'refresh' on the screen.
C# Code :
private ICollectionView _ProductInfoView;
public ICollectionView ProductInfoView
{
get{return this._ProductInfoView;}
set
{
this._ProductInfoView=value;
this.onPropertyChnage("ProductInfoView");
}
}
private void RibbonSetupProduct_Click(object sender, System.Windows.RoutedEventArgs e)
{
this.hidePanels();
new Task(() =>
{
this.Dispatcher.Invoke(new Action(() =>
{
ObservableCollection<ModelProductInformation> productInfoCollection = new ObservableCollection<ModelProductInformation>(from ProductInfo in new GTS_ERPEntities().ProductInformations select new ModelProductInformation { ProductID = ProductInfo.ProductID, ProductName = ProductInfo.ProductName , Remark=ProductInfo.Remark});
this.ProductInfoView = CollectionViewSource.GetDefaultView(productInfoCollection);
new ProductInfoSearch(ProductInfoView, this.TestTextBox);
}
), DispatcherPriority.DataBind);
}
).Start();
this.PanelProducts.Visibility = Visibility.Visible;
}
class ProductInfoSearch
{
public ProductInfoSearch(ICollectionView filteredList, TextBox textEdit)
{
string filterText = string.Empty;
filteredList.Filter = delegate(object obj)
{
if (String.IsNullOrEmpty(filterText))
{
return true;
}
ModelProductInformation str = obj as ModelProductInformation;
if (str.ProductName==null)
{
return true;
}
if (str.ProductName.ToUpper().Contains(filterText.ToUpper()))
{
return true;
}
else
{
return false;
}
};
textEdit.TextChanged += delegate
{
filterText = textEdit.Text;
filteredList.Refresh();
};
}
}
XAML :
<dxe:ListBoxEdit x:Name="ProductInfoList" Margin="1.666,1,8,8" Grid.Column="2" Grid.Row="2" Grid.RowSpan="5" DisplayMember="ProductName" DataContext="{Binding ProductInfoView, ElementName=window}" ItemsSource="{Binding}"/>
I guess my problem is either Data Binding or inside Task().
i would make the ObservableCollection a private field and just create the instance once and also just create the ICollectionView once. to add new data you can use clear and add on your Collection - try it, it works for me.
private ObservableCollection<ModelProductInformation> productInfoCollection;
//ctor, just once in the constructor
this.productInfoCollection = new ObservableCollection<ModelProductInformation>();
this.ProductInfoView = CollectionViewSource.GetDefaultView(productInfoCollection);
new ProductInfoSearch(ProductInfoView, this.TestTextBox);
private void RibbonSetupProduct_Click(object sender, System.Windows.RoutedEventArgs e)
{
this.hidePanels();
new Task(() =>
{
this.Dispatcher.Invoke(new Action(() =>
{
var yourData = from ProductInfo in new GTS_ERPEntities().ProductInformations select new ModelProductInformation { ProductID = ProductInfo.ProductID, ProductName = ProductInfo.ProductName , Remark=ProductInfo.Remark};
//if you wanna change the collection, simple clear and add(or create AddRange extension)
this.productInfoCollection.Clear();
foreach(var data in yourData)
{ this.productInfoCollection.Add(data);}
}
), DispatcherPriority.DataBind);
}
).Start();
this.PanelProducts.Visibility = Visibility.Visible;
}

C# Custom Class using INotifyPropertyChanged not notifying on Assignment

I have a class that implements the INotifyPropertyChanged interface.
If I create a new instance of the object, the PropertyChanged event gets set after I retrieve a value from it.
Example:
MyItem itm = new MyItem(); //MyItem.PropertyChanged == null
string test = itm.Value; //MyItem.PropertyChanged != null
If I assign itm the value of another MyItem, the PropertyChanged event remains null.
Example:
itm = (MyItem)cboMyItemsCombobox.SelectedItem; // Properties for itm change to the values
// of the selected item, but PropertyChanged
// == null
I believe the problem lies partially in my custom constructor for the class, but I'm not entirely sure.
The goal is to have a variable to hold data for a record, called mnuitm, that is bound to
3 textbox objects. When the text in a textbox changes, the change is made to the property in mnuitm. When the property in mnuitm is changed, the change is made in the textbox.
This works if I create a new MenuItem and assign the values individually, but does not work if I assign an already populated MenuItem to mnuitm.
Here is my actual code for (hopefully) more clearity on the issue.
public partial class frmMenuItems : Form
{
private class MenuItem : INotifyPropertyChanged
{
private Int32 mid;
private string txt;
private string url;
private string scp;
public MenuItem() { }
public MenuItem(Int32 id, string txt, string url, string scp)
{
ID = id;
Text = txt;
URL = url;
Script = scp;
}
public Int32 ID
{
get
{
return mid;
}
set
{
if (mid != value)
{
mid = value;
NotifyPropertyChanged("ID");
}
}
}
public string Text {
get
{
return txt;
}
set
{
if (txt != value)
{
txt = value;
NotifyPropertyChanged("Text");
}
}
}
public string URL {
get
{
return url;
}
set
{
if (url != value)
{
url = value;
NotifyPropertyChanged("URL");
}
}
}
public string Script {
get
{
return scp;
}
set
{
if (scp != value)
{
scp = value;
NotifyPropertyChanged("Script");
}
}
}
public void Clear()
{
ID = 0;
Text = "";
URL = "";
Script = "";
}
public override string ToString()
{
return Text;
}
private void NotifyPropertyChanged(string inf)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(inf));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
private MenuItem mnuitm;
private MySqlConnection sqlcon;
public frmMenuItems()
{
InitializeComponent();
}
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnCancel_Click(object sender, EventArgs e)
{
mnuitm.Clear();
}
private void frmMenuItems_Load(object sender, EventArgs e)
{
string constr = "server={0};uid={1};pwd={2};database={3};";
DBItem dbi = CountyDataManager.CountyData.DBConnection;
constr = string.Format(constr, [MyHost], [MyUsername], [MyPassword], [MyDatabase]);
sqlcon = new MySqlConnection(constr);
sqlcon.Open();
mnuitm = new MenuItem();
SetBindings();
RefreshList();
}
private void SetBindings()
{
txtMenuText.DataBindings.Clear();
txtURL.DataBindings.Clear();
txtScript.DataBindings.Clear();
txtMenuText.DataBindings.Add("Text", mnuitm, "Text");
txtURL.DataBindings.Add("Text", mnuitm, "URL");
txtScript.DataBindings.Add("Text", mnuitm, "Script");
}
private void RefreshList()
{
using (MySqlCommand cmd = new MySqlCommand("SELECT `menuid`,`menutext`,`url`,`script` FROM tblindexmenu ORDER BY `menutext`", sqlcon))
{
lstMenuItems.Items.Clear();
using (MySqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
lstMenuItems.Items.Add(new MenuItem(Int32.Parse(rdr[0].ToString()), rdr[1].ToString(),rdr[2].ToString(),rdr[3].ToString()));
}
}
}
}
private void frmMenuItems_FormClosing(object sender, FormClosingEventArgs e)
{
sqlcon.Close();
}
private void lstMenuItems_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstMenuItems.SelectedIndex > -1)
{
mnuitm = (MenuItem)lstMenuItems.SelectedItem;
}
}
}
After receiving feedback, I made the following changes:
Added CopyFrom() to MenuItem
public void CopyFrom(MenuItem itm)
{
this.ID = itm.ID;
this.URL = itm.URL;
this.Text = itm.Text;
this.Script = itm.Script;
}
I then modified the SelectedIndexChanged code to use the new function
mnuitm.CopyFrom((MenuItem)lstMenuItems.SelectedItem);
This is by design. When you write
itm = (MyItem)cboMyItemsCombobox.SelectedItem;
you haven't changed any of the properties of the MenuItem itm used to point to, rather you changed the MenuItem itm points to.
One option for what you need is add a function to MenuItem that looks like
SetFromOtherMenuItem(MenuItem other)
{
this.Url = other.Url
//etc
}
Again, PropertyChanged means that a property on some instance has changed. In your case, only one of the variables pointing to that instance changed (to point to a different instance).

Editing newly added row in Silverlight 3 DataGrid using MVVM

I am trying to use the Silverlight 3.0 DataGrid with the MVVM design pattern. My page has a DataGrid and a button that adds an item to the collection in the VM using a command (from the Composite Application Library). This works fine, and the new item is displayed and selected.
The problem I can't solve is how to begin editing the row. I want the new row to be immediately editable when the user clicks the Add button i.e. focus set to the DataGrid and the new row in edit mode.
This is the XAML in the view:
<Grid x:Name="LayoutRoot">
<StackPanel>
<data:DataGrid ItemsSource="{Binding DataView}"/>
<Button cmd:Click.Command="{Binding AddItemCommand}" Content="Add" />
</StackPanel>
</Grid>
The code behind has one line of code that creates an instance of the VM and sets the DataContext of the view.
The VM code is:
public class VM
{
public List<TestData> UnderlyingData { get; set; }
public PagedCollectionView DataView { get; set; }
public ICommand AddItemCommand { get; set; }
public VM()
{
AddItemCommand = new DelegateCommand<object>(o =>
{
DataView.AddNew();
});
UnderlyingData = new List<TestData>();
UnderlyingData.Add(new TestData() { Value = "Test" });
DataView = new PagedCollectionView(UnderlyingData);
}
}
public class TestData
{
public string Value { get; set; }
public TestData()
{
Value = "<new>";
}
public override string ToString()
{
return Value.ToString();
}
}
What would be the best way to solve this problem using the MVVM design pattern?
I faced the same issue. I've introduced interface ISupportEditingState:
public interface ISupportEditingState
{
EditingState EditingState { get; set; }
}
My VM implements it. And then I wrote this behaviour to synchronise editing state of DataGrid and my VM:
public class SynchroniseDataGridEditingStateBehaviour : Behavior<DataGrid>
{
public static readonly DependencyProperty EditingStateBindingProperty =
DependencyProperty.Register("EditingStateBinding", typeof(ISupportEditingState),
typeof(SynchroniseDataGridEditingStateBehaviour), new PropertyMetadata(OnEditingStateBindingPropertyChange));
private bool _attached;
private bool _changingEditingState;
public ISupportEditingState EditingStateBinding
{
get { return (ISupportEditingState)GetValue(EditingStateBindingProperty); }
set { SetValue(EditingStateBindingProperty, value); }
}
private static void OnEditingStateBindingPropertyChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var b = d as SynchroniseDataGridEditingStateBehaviour;
if (b == null)
return;
var oldNotifyChanged = e.OldValue as INotifyPropertyChanged;
if (oldNotifyChanged != null)
oldNotifyChanged.PropertyChanged -= b.OnEditingStatePropertyChanged;
var newNotifyChanged = e.NewValue as INotifyPropertyChanged;
if (newNotifyChanged != null)
newNotifyChanged.PropertyChanged += b.OnEditingStatePropertyChanged;
var newEditingStateSource = e.NewValue as ISupportEditingState;
if (newEditingStateSource.EditingState == EditingState.Editing)
{
// todo: mh: decide on this behaviour once again.
// maybe it's better to start editing if selected item is already bound in the DataGrid
newEditingStateSource.EditingState = EditingState.LastCancelled;
}
}
private static readonly string EditingStatePropertyName =
CodeUtils.GetPropertyNameByLambda<ISupportEditingState>(ses => ses.EditingState);
private void OnEditingStatePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_changingEditingState || !_attached || e.PropertyName != EditingStatePropertyName)
return;
_changingEditingState = true;
var editingStateSource = sender as ISupportEditingState;
if (editingStateSource == null)
return;
var grid = AssociatedObject;
var editingState = editingStateSource.EditingState;
switch (editingState)
{
case EditingState.Editing:
grid.BeginEdit();
break;
case EditingState.LastCancelled:
grid.CancelEdit();
break;
case EditingState.LastCommitted:
grid.CommitEdit();
break;
default:
throw new InvalidOperationException("Provided EditingState is not supported by the behaviour.");
}
_changingEditingState = false;
}
protected override void OnAttached()
{
var grid = AssociatedObject;
grid.BeginningEdit += OnBeginningEdit;
grid.RowEditEnded += OnEditEnded;
_attached = true;
}
protected override void OnDetaching()
{
var grid = AssociatedObject;
grid.BeginningEdit -= OnBeginningEdit;
grid.RowEditEnded -= OnEditEnded;
_attached = false;
}
void OnEditEnded(object sender, DataGridRowEditEndedEventArgs e)
{
if (_changingEditingState)
return;
EditingState editingState;
if (e.EditAction == DataGridEditAction.Commit)
editingState = EditingState.LastCommitted;
else if (e.EditAction == DataGridEditAction.Cancel)
editingState = EditingState.LastCancelled;
else
return; // if DataGridEditAction will ever be extended, this part must be changed
EditingStateBinding.EditingState = editingState;
}
void OnBeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
if (_changingEditingState)
return;
EditingStateBinding.EditingState = EditingState.Editing;
}
}
Works ok for me, hope it helps.
Whenever you talk about directly accessing ui components, your kinda missing the point of mvvm. The ui binds to the viewmodel, so find a way to alter the viewmodel instead.

Categories

Resources