How to use an observable collection and notifyproperty changed - c#

I am new to wpf and i am currently trying to play with grid and adding subtracting,removing elements to a db table which eventually binds to a grid.
So select from grid, update observable collection, refresh.
I am failing to understand how can i use change notifications of observable collection.
Here is my code
Class which binds to grid
public class students
{
ObservableCollection<GetStudents_Result> stdb = new ObservableCollection<GetStudents_Result>();
//public event NotifyCollectionChangedEventHandler CollectionChanged;
public students()
{
AbcdEntities abcdEnt=new AbcdEntities();
List<GetStudents_Result> studentColl = abcdEnt.GetStudents().ToList();
foreach (var item in studentColl)
{
stdb.Add(item);
}
}
//public void onCollectionChange(object sender,NotifyCollectionChangedEventHandler e)
//{
//}
public ObservableCollection<GetStudents_Result> std {get {return stdb;}}
}
my xaml.
<Canvas>
<TextBox Height="23" Canvas.Top="5" Canvas.Left="10" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
<TextBox Height="23" Canvas.Top="30" Canvas.Left="10" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" />
<Button Canvas.Left="90" Canvas.Top="65" Content="Remove" Click="button2_Click" Height="23" Name="button2" Width="75" />
<Button Canvas.Left="10" Canvas.Top="65" Content="Save" Height="23" Name="button1" Width="75" Click="button1_Click" />
<ListView Name="listviewStudents" Canvas.Top="100" ItemsSource="{Binding std}" SelectionChanged="ListView_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="fname" DisplayMemberBinding="{Binding Path=fname}"></GridViewColumn>
<GridViewColumn Header="lname" DisplayMemberBinding="{Binding Path=lname}"></GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=address}"></GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=phno}"></GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=radio}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Canvas>
my code behind
public MainWindow()
{
InitializeComponent();
students std = new students();
this.DataContext = std;
}
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
GetStudents_Result selectedItem = listviewStudents.SelectedItem as GetStudents_Result;
textBox1.Text = selectedItem.fname;
textBox2.Text = selectedItem.lname;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
GetStudents_Result selecteditem = listviewStudents.SelectedItem as GetStudents_Result;
selecteditem.fname = textBox1.Text;
selecteditem.lname = textBox2.Text;
listviewStudents.Items.Refresh();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
listviewStudents.Items.Remove(listviewStudents.SelectedItem);
listviewStudents.Items.Refresh();
}
}
}
pardon for any stupid mistakes..

There a a coulpe of problems here, you should not have to touch the UI controls from the code behind, you should be using data binding.
Here is a working example of model binding based on your post.
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
// set the DataContext to the code in this window
this.DataContext = this;
// create youe Student model
StudentModel = new students();
}
// Create a public property of your student Model
private students _studentModel;
public students StudentModel
{
get { return _studentModel; }
set { _studentModel = value; NotifyPropertyChanged("StudentModel"); }
}
// create a public property to use as the selected item from your models "std" collection
private GetStudents_Result _selectedResult;
public GetStudents_Result SelectedResult
{
get { return _selectedResult; }
set { _selectedResult = value; NotifyPropertyChanged("SelectedResult"); }
}
private void button2_Click(object sender, RoutedEventArgs e)
{
// if you want to remove an item you just have to remove it from
// the model, the INotifyPropertyChanged interface will notify the UI
// to update, no need to call Refresh, same works for Add etc
StudentModel.std.Remove(SelectedResult);
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Now in the xaml we can bind the ListView to your StudentModel collection and the SelectedResult
<ListView ItemsSource="{Binding StudentModel.std}" SelectedItem="{Binding SelectedResult}" >
And for the TextBoxes you can bind to the SelectedResult so it will update the details for you
Note: In this example it updates the SelectedResult when the text changes, you can change this as you wish.
<TextBox Text="{Binding SelectedResult.Fname, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding SelectedResult.Lname, UpdateSourceTrigger=PropertyChanged}" />
So now when you select an item from the ListView these TextBoxes will be populated, and when they are changed the SelectedResult item will be changed.
Now, for adding and removing Items in your ListView, you just have to add and remove from your StudentModelcollection (StudentModel.std).
private void button2_Click(object sender, RoutedEventArgs e)
{
StudentModel.std.Remove(SelectedResult);
}
Note: This event handler should be an ICommand binding, but i will let your search for that :)
Here is a Full example tha hopefully helps explaining the basics of WPF MVVM
Code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
StudentModel = new students();
}
private students _studentModel;
public students StudentModel
{
get { return _studentModel; }
set { _studentModel = value; NotifyPropertyChanged("StudentModel"); }
}
private GetStudents_Result _selectedResult;
public GetStudents_Result SelectedResult
{
get { return _selectedResult; }
set { _selectedResult = value; NotifyPropertyChanged("SelectedResult"); }
}
private void button2_Click(object sender, RoutedEventArgs e)
{
StudentModel.std.Remove(SelectedResult);
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class students
{
public students()
{
std = new ObservableCollection<GetStudents_Result>();
for (int i = 0; i < 100; i++)
{
std.Add(new GetStudents_Result { Fname = "FirstName" + i, Lname = "LasrName" + i });
}
}
public ObservableCollection<GetStudents_Result> std { get; set; }
}
public class GetStudents_Result : INotifyPropertyChanged
{
private string _fname;
private string _lname;
public string Fname
{
get { return _fname; }
set { _fname = value; NotifyPropertyChanged("Fname"); }
}
public string Lname
{
get { return _lname; }
set { _lname = value; NotifyPropertyChanged("Lname"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml:
<Canvas>
<TextBox Text="{Binding SelectedResult.Fname, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding SelectedResult.Lname, UpdateSourceTrigger=PropertyChanged}" />
<Button Canvas.Left="90" Canvas.Top="65" Content="Remove" Click="button2_Click" Height="23" Name="button2" Width="75" />
<Button Canvas.Left="10" Canvas.Top="65" Content="Save" Height="23" Name="button1" Width="75" />
<ListView Name="listviewStudents" Canvas.Top="100" ItemsSource="{Binding StudentModel.std}" SelectedItem="{Binding SelectedResult}" >
<ListView.View>
<GridView>
<GridViewColumn Header="fname" DisplayMemberBinding="{Binding Path=Fname}"></GridViewColumn>
<GridViewColumn Header="lname" DisplayMemberBinding="{Binding Path=Lname}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Canvas>
I hope this info helps :)

Okay first, I apologize if I do a poor job answering this. It's my first attempt.
So it looks like you've got the right idea, and most of what you've got will work. But it looks like you may have forgotten to implement INotifyPropertyChanged. And you might just consider using something like List<GetStudent_Result> instead, there is a lot less overhead than an ObservableCollection because you'll be implementing the NotifyPropertyChanged stuff yourself.
public class students : INotifyPropertyChanged
{
#region PropertyChanged EventHandler
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged(String Property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
#endregion
private List<GetStudents_Result> stdb;
public List<GetStudents_Result> std
{
get { return stdb; }
set { stdb = value; NotifyPropertyChanged("std"); }
}
...

Related

The right way to implement Weak Event Pattern for shared property MVVM

I'm preparing an application that will have multiple objects, each one will have an integer id and string data. I want the data of the objects with the same id to be the same. I will call this object SharedObject (this object acts like a ViewModel for some database table).
One may say it is very easy. You can create a single object and reference it each time you need it. But actually this way will not work for me since I can't predict when and where the object is created.
So to handle this problem I decided to put a static event in the SharedObject class and call it when any SharedObject data is changed (like PropertyChanged event in INotifyPropertyChanged but mine is static. This will make it visible to each instance of SharedObject), but the problem is that I end up with a program full of memory leaks!!
Last night I sorted out that I had to use weak event pattern to solve the memory leak problem.
I searched the net over the last night for full implementation of weak event pattern but I ended up with nothing or deleted pages.
Finally I figured out how to do it with weak event pattern and
I created a test project to test this approach, but it did not work,
the ReceiveWeakEvent event couldn't be called.
Am I missing something?
Or what is the right way to implement this pattern?
By the way I'm using .net 4 because I need my application to support Win XP (this is what my customer needs, don't ask me about it), so I can't use the generic WeakEventManager class.
Sorry for the long post but this problem was about to make me tear my hair off, and thanks in advance.
Here is the full application code:
SharedObject class:
public class SharedObject :INotifyPropertyChanged,IWeakEventListener
{
static WaekEvent sheardEvent = new WaekEvent();
public event PropertyChangedEventHandler PropertyChanged;
public int ID { get; private set; }
public SharedObject(int id)
{
this.ID = id;
SharedWeakEventManager.AddListener(sheardEvent, this);
}
string data;
public string Data
{
get
{
return data;
}
set
{
if(value != data)
{
data = value;
OnPropertyChanged("Data");
sheardEvent.RiseSharedEvent(this, new PropertyChangedEventArgs("Data"));
}
}
}
protected virtual void OnPropertyChanged(string propName)
{
PropertyChangedEventHandler handler;
lock (this)
{
handler = PropertyChanged;
}
if (handler != null)
handler.Invoke(this, new PropertyChangedEventArgs(propName));
}
void OnSharedPropertyChaingo(object sender,PropertyChangedEventArgs e)
{
SharedObject s = sender as SharedObject;
if (s == null || s.ID != ID)
return;
data = s.Data;
OnPropertyChanged("Data");
}
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if(managerType == typeof(SharedWeakEventManager) && sender is SharedObject)
{
OnSharedPropertyChaingo(sender, e as PropertyChangedEventArgs);
}
return false;
}
}
WaekEvent and SharedWeakEventManager classes:
public class SharedObject
{
public event EventHandler SharedEvent;
public void RiseSharedEvent(object sender, EventArgs args)
{
if (SharedEvent != null)
SharedEvent.Invoke(sender, args);
}
}
public class SharedWeakEventManager : WeakEventManager
{
public static SharedWeakEventManager CurrentManager
{
get
{
var manager_type = typeof(SharedWeakEventManager);
var manager = WeakEventManager.GetCurrentManager(manager_type) as SharedWeakEventManager;
if (manager == null)
{
manager = new SharedWeakEventManager();
WeakEventManager.SetCurrentManager(manager_type, manager);
}
return manager;
}
}
public static void AddListener(WaekEvent source, IWeakEventListener listener)
{
CurrentManager.ProtectedAddListener(source, listener);
}
public static void RemoveListener(WaekEvent source, IWeakEventListener listener)
{
CurrentManager.ProtectedRemoveListener(source, listener);
}
protected override void StartListening(object source)
{
((WaekEvent)source).SharedEvent += this.DeliverEvent;
}
protected override void StopListening(object source)
{
((WaekEvent)source).SharedEvent -= this.DeliverEvent;
}
}
the XAML part of the MainWindow:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView x:Name="View" Margin="10,49,10,10" SelectionChanged="ListView_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" Width="100" DisplayMemberBinding="{Binding ID}"/>
<GridViewColumn Header="Data" Width="100" DisplayMemberBinding="{Binding Data}"/>
</GridView>
</ListView.View>
</ListView>
<Button Content="New" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10,13.01,0,0" Click="Button_Click"/>
<TextBox x:Name="IDText" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="92" Margin="136.2,11,0,0"/>
<TextBox x:Name="DataText" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="92" Margin="280.799,11,0,0" TextChanged="DataText_TextChanged"/>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="ID" VerticalAlignment="Top" Margin="119.593,14,0,0"/>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Data" VerticalAlignment="Top" Margin="251.106,15.01,0,0"/>
<Button Content="Remove" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="377.799,12,0,0" Click="Button_Click_1"/>
</Grid>
MainWindow class:
public partial class MainWindow : Window
{
private ObservableCollection<SharedObject> sharedObjects;
public ObservableCollection<SharedObject> SharedObjects
{
get
{
if (sharedObjects == null)
sharedObjects = new ObservableCollection<SharedObject>();
return sharedObjects;
}
}
public SharedObject SelelectedObject { get; set; }
public MainWindow()
{
InitializeComponent();
View.ItemsSource = SharedObjects;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int id;
if (!int.TryParse(IDText.Text, out id))
return;
SharedObject x = new SharedObject(id);
x.Data = DataText.Text;
SharedObjects.Add(x);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
if (SelelectedObject == null)
return;
SharedObjects.Remove(SelelectedObject);
SelelectedObject = null;
}
private void DataText_TextChanged(object sender, TextChangedEventArgs e)
{
if (SelelectedObject != null)
SelelectedObject.Data = DataText.Text;
}
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelelectedObject = View.SelectedItem as SharedObject;
if (SelelectedObject == null)
return;
IDText.Text = SelelectedObject.ID.ToString();
DataText.Text = SelelectedObject.Data;
}
}

Toggle List Box Display In WPF

I have a List Box in my WPF app. Here is the xaml code:
<ListBox Grid.Row="4" Grid.Column="1" Visibility="{Binding lbIsVisible}">
<ListBoxItem>
<CheckBox>
<TextBlock>CITRUS EXPRESS</TextBlock>
</CheckBox>
</ListBoxItem>
<ListBoxItem>
<CheckBox>
<TextBlock>APL CALIFORNIA</TextBlock>
</CheckBox>
</ListBoxItem>
<ListBoxItem>
<CheckBox>
<TextBlock>IS JAPAN</TextBlock>
</CheckBox>
</ListBoxItem>
</ListBox>
<CheckBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" x:Name="chkSelectVessel" Checked="chkSelectVessel_Checked">
<TextBlock Text="Select Vessel"></TextBlock>
</CheckBox>
I'm trying to toggle the visibility of the list box.
Here is the C# code.
public partial class ConfigSetting : Window
{
public string lbIsVisible { get; set; }
public ConfigSetting()
{
InitializeComponent();
DataContext = this;
lbIsVisible = "Hidden";
}
private void chkSelectVessel_Checked(object sender, RoutedEventArgs e)
{
this.lbIsVisible = "Visible";
}
}
But it doesn't seem to work. Can any one point where I'm going wrong?
You should use INotifyPropertyChanged Interface like this:
public partial class ConfigSetting : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string lbIsVisible;
public string LbIsVisible
{
get { return lbIsVisible; }
set
{
if (lbIsVisible != value)
{
lbIsVisible = value;
OnPropertyChanged("LbIsVisible");
}
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ConfigSetting()
{
InitializeComponent();
LbIsVisible = "Hidden";
DataContext = this;
}
private void chkSelectVessel_Checked(object sender, RoutedEventArgs e)
{
LbIsVisible = "Visible";
}
private void ChkSelectVessel_OnUnchecked(object sender, RoutedEventArgs e)
{
LbIsVisible = "Hidden";
}
}
And in the XAML bind to LbIsVisible property:
<ListBox Visibility="{Binding LbIsVisible}">
Also add Unchecked event to your CheckBox :
<CheckBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Center"
x:Name="chkSelectVessel" Checked="chkSelectVessel_Checked" Unchecked="ChkSelectVessel_OnUnchecked">

Remove row from DataGridView in WPF

run into a problem, i have a datagrid, columns as follow
One, Two, Multiply
i can delete row buy clicking del KEY on the keyboard but when im trying to program my own remove function it does not work
and the message im getting is
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll
Additional information: Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
Here is my code:
private void buttonRemove_Click(object sender, RoutedEventArgs e)
{
NumberData.Items.Remove(NumberData.SelectedItems);
}
public class Numbers : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private double? _one;
private double? _two;
private double? _multiply;
public double? One { get { return _one; } set { _one = value; UpdateValue(); } }
public double? Two { get { return _two; } set { _two = value; UpdateValue(); } }
public double? Multiply
{
get { return _multiply; }
set
{
_multiply = value; UpdateValue();
}
}
public Numbers(double? pOne, double? pTwo)
{
_one = pOne;
_two = pTwo;
_multiply = GetMultiply();
}
private void UpdateValue()
{
_multiply = GetMultiply();
OnPropertyChanged("One");
OnPropertyChanged("Two");
OnPropertyChanged("Multiply");
}
private double? GetMultiply()
{
return _one * _two;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public class Collection : ObservableCollection<Numbers>
{
public ObservableCollection<Numbers> Numbers { get; set; }
public Collection()
{
Numbers = new ObservableCollection<Numbers>();
//Numbers.Add(new Numbers(1, 2));
//Numbers.Add(new Numbers(2, 2));
}
public void AddNumbers(double pOne, double pTwo)
{
Numbers.Add(new Numbers(pOne,pTwo));
}
public void AddNumbersRow()
{
Numbers.Add(new Numbers(null, null));
}
}
<Grid>
<DataGrid x:Name="NumberData" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" ItemsSource="{Binding}" AutoGenerateColumns="False" LostFocus="StockData_LostFocus" >
<DataGrid.Columns >
<DataGridTextColumn Header="Number One" Width="100" Binding="{Binding One, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:C\}}" />
<DataGridTextColumn Header="Number Two" Width="100" Binding="{Binding Two, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:C\}}" />
<DataGridTextColumn Header="Total" Width="100" Binding="{Binding Multiply, BindsDirectlyToSource=True, Mode=TwoWay, StringFormat=\{0:C\}, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
<Button x:Name="buttonSave" Content="Save" HorizontalAlignment="Left" Margin="411,11,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<Button x:Name="buttonAddRow" Content="Add Row" HorizontalAlignment="Left" Margin="411,49,0,0" VerticalAlignment="Top" Width="75" Click="buttonAddRow_Click"/>
<Button x:Name="buttonRemove" Content="Remove" HorizontalAlignment="Left" Margin="411,88,0,0" VerticalAlignment="Top" Width="75" Click="buttonRemove_Click"/>
</Grid>
It looks like you have everything set up right, with a single oddity:
<DataGrid ItemsSource="{Binding}">
Not sure why the ItemsSource is the DataContext. The DataContext should be a View Model class with some ObservableCollection property that you then bind to.
But beyond that, since you said your code was working; you shouldn't be deleting items manually like that.
Your button should be bound to a command, that goes to the view model and modifies the bound collection. A sample would look like:
myObservableCollection.RemoveAt(0);
Since its an ObservableCollection, the modification will propagate to the UI.
Thanks a lot, i got the idea from your answers , here is what i have done to remove the item
if (NumberData.SelectedIndex == -1)
{
System.Windows.MessageBox.Show("nothing selected");
}
else
{
int indexSelected = Convert.ToInt32(NumberData.SelectedIndex);
mycollection.Numbers.RemoveAt(indexSelected);
}

EASY way to refresh ListBox in WPF?

I have created a simple form that inserts/updates/deletes a values for Northwind Customers.
Everything works fine, except in order to see a results, I have to close it, and reopen again.
My form looks like this :
I've searched tens of articles on how to refresh ListBox, but all of those use interface implementing, or using DataSets, and stuff I have never heard of and cannot implement. It's a very simple project, using simple procedures. Is there an easy way to refresh the list of customers without adding many lines of code?
The simple answer is: myListBox.Items.Refresh();
Are you using ObservableCollection and does your model implement INotifyPropertyChanged these two things will automaticly update the ListBox on any change. no need to explicitly refresh the list.
Here is a small example of using ObservableCollection and INotifyPropertyChanged, obviously you will populate your ObservableCollection from your SQL database.
Window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<MyModel> _list = new ObservableCollection<MyModel>();
private MyModel _selectedModel;
public MainWindow()
{
InitializeComponent();
List.Add(new MyModel { Name = "James", CompanyName = "StackOverflow"});
List.Add(new MyModel { Name = "Adam", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Chris", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Steve", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Brent", CompanyName = "StackOverflow" });
}
public ObservableCollection<MyModel> List
{
get { return _list; }
set { _list = value; }
}
public MyModel SelectedModel
{
get { return _selectedModel; }
set { _selectedModel = value; NotifyPropertyChanged("SelectedModel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Name="UI">
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=List}" SelectedItem="{Binding ElementName=UI, Path=SelectedModel}" Margin="0,0,200,0" DisplayMemberPath="DisplayMember" SelectedIndex="0" />
<StackPanel HorizontalAlignment="Left" Height="100" Margin="322,10,0,0" VerticalAlignment="Top" Width="185">
<TextBlock Text="Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Company Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.CompanyName, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
Model
public class MyModel : INotifyPropertyChanged
{
private string _name;
private string _companyName;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public string CompanyName
{
get { return _companyName; }
set { _companyName = value; NotifyPropertyChanged("CompanyName"); }
}
public string DisplayMember
{
get { return string.Format("{0} ({1})", Name, CompanyName); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("DisplayMember"));
}
}
}
In this case any edit to properties will Update your list instantly, also will update when new Items are added/removed.
How about calling ListBox.UpdateLayout?
Of course you also need to update the particular item(s) so that it returns the updated string from the ToString method.
UPDATE: I think you also need to call ListBox.InvalidateArrange before you call ListBox.UpdateLayout.
Use INotifyPropertyChanged is the best way, refresh the entire list is not a good idea.
Main entrance:
public partial class MainWindow : Window
{
private BindingList<FoodModel> foodList = new BindingList<FoodModel>();
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
foodList.Add(new FoodModel { foodName = "apple1" });
foodList.Add(new FoodModel { foodName = "apple2" });
foodList.Add(new FoodModel { foodName = "apple3" });
FoodListBox.ItemsSource = foodList;
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
foodList[0].foodName = "orange";
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
FoodListBox.Items.Refresh();
}
}
Model:
public class FoodModel: INotifyPropertyChanged
{
private string _foodName;
public string foodName
{
get { return _foodName; }
set
{
if (_foodName != value)
{
_foodName = value;
PropertyChanged(this, new PropertyChangedEventArgs("foodName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
XAML:
<ListBox HorizontalAlignment="Center" Name="FoodListBox" VerticalAlignment="Top" Width="194" Height="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding foodName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Binding to a WPF combo box in a list view (2-way)

I have this problem of having to bind the selected value of a combo box embedded inside a list view. I have no trouble in displaying items in the combo box. However, I wish I had a way to dictate what the combo box should display (from among the items that it holds) on the click of a button. I think there are several posts on this issue, however, I am not able to get exactly what I want. Here is my code.
XAML:
<Grid>
<StackPanel Orientation="Vertical">
<ListView
x:Name="OptionsListView"
ItemsSource="{Binding MyObjectCollection}">
<ListView.Resources>
<DataTemplate x:Key="comboBoxTemplate">
<ComboBox
Margin="0,3"
x:Name="MyTypeComboBox"
SelectedValue="{Binding Path=SelectedType, Mode=TwoWay}">
<ComboBoxItem Content="ABC"/>
<ComboBoxItem Content="DEF"/>
<ComboBoxItem Content="XYZ"/>
</ComboBox>
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Text-Sample"
Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Combo-Sample"
Width="100"
CellTemplate="{StaticResource comboBoxTemplate}" />
</GridView>
</ListView.View>
</ListView>
<Button Click="Button_Click">Click Me!</Button>
</StackPanel>
</Grid>
C# Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
OptionsListView.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//Something here that dictates what should be displayed in the combo box
}
List<MyObject> myObjectCollection = new List<MyObject>();
public List<MyObject> MyObjectCollection
{
get
{
myObjectCollection.Add(new MyObject("One"));
myObjectCollection.Add(new MyObject("Two"));
return myObjectCollection;
}
}
}
public class MyObject : INotifyPropertyChanged
{
private string _name;
public MyObject(string name)
{
// TODO: Complete member initialization
this._name = name;
}
public string Name
{
get
{
return _name;
}
}
string selectedType = string.Empty;
public string SelectedType
{
get
{
return selectedType;
}
set
{
selectedType = value;
this.NotifyChange("SelectedType");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyChange(params object[] properties)
{
if (PropertyChanged != null)
{
foreach (string p in properties)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
#endregion
}
I would be glad if someone could help me crack this..
Thanks
Ram
I'm not sure if I misunderstanding your question. I think your issue is about the reference issue. I changed your code a little and it works when click on the button.
See the code below.
XAML:
<ComboBox Margin="0,3"
x:Name="MyTypeComboBox"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=ListBox},Path=DataContext.Sources}"
SelectedValue="{Binding Path=SelectedType, Mode=TwoWay}">
</ComboBox>
C# code:
private Collection<string> sources = new Collection<string>() { "ABC", "DEF", "XYZ" };
public Collection<string> Sources { get { return sources; } }
private void Button_Click(object sender, RoutedEventArgs e)
{
myObjectCollection[0].SelectedType = Sources[0];
myObjectCollection[1].SelectedType = Sources[2];
}
How about
foreach (ComboBox c in OptionsListView.Items)
{
c.SelectedValue = "Put your value here";
}
This should do the work, if you have other objects than comboboxes inside you can add a
if (c is ComboBox)
to ensure that you are working on the right object

Categories