WPF MVVM add item to combobox and change selecteditem - c#

SO I have a WPF MVVM application. When I press a button, I want a new entry to be added to the dropdown in a combobox, and for the combobox's selected item to be set to that item. I'm able to get the item added to the dropdown through an ObservableCollection, but I can't seem to bind the SelectedItem properly. I have tried:
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
And putting the SelectedItem directly in the ViewModel implementing INotifyPropertyChanged in the ViewModel. But this does not work. Any ideas?
EDIT: I should also add that the OnNotifyPropertyChange event does fire correctly when I expect it to, so I'm not sure what's going on. I also tried using UpdateSourceTrigger=PropertyChanged to no avail.

Just a small advice to avoid such a situation where you write the name of the property incorect.
If you're using .net 4.5 you can use the CallerMemberName-Attribute in your OnPropertyChanged-Method. This looks like:
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
Then you're property looks something like
public bool MyProperty
{
get{ return myProperty; }
set
{
myProperty=value;
OnPropertyChanged();
}
}
You also can write a method which extracts you the propertyname out of a lambda-expression. The method in a base-class looks like:
public static class Helper
{
public static string GetPropertyName<T>(Expression<Func<T>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
}
The usage in a property than looks like:
public bool MyProperty
{
get{ return myProperty; }
set
{
myProperty = value;
OnPropertyChanged(Helper.GetPropertyName(() => MyProperty));
}
}
With this approach you have a compile-time-check of your property-name.

Thanks guys, I spelled the property wrong in the OnNotifyPropertyChanged...Good grief.

You can also use
OnPropertyChanged(nameof(MyProperty));
to avoid missspellings.
I started to use Fody with PropertyChanged which injects the OnPropertyChanged Code automatically saving you some typing work and makes the code look nice and clean.

Related

WPF: Binding TextBox Text to a sub-element of a Property with WCF?

I have a TextBox which I'm trying to bind to a element of a table property 'regimeAlias' is a column with the tbRegimes table which I have mapped with Entity Framework:
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
Exposed property in my ViewModel:
private tbRegime _NewRegime;
public tbRegime NewRegime
{
get { return _NewRegime; }
set
{
_NewRegime = value;
OnPropertyChanged("NewRegime");
}
}
Lastly, here's the WCF Service Reference auto-generated code class:
public partial class tbRegime : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
//blah blah blah
[System.Runtime.Serialization.DataMemberAttribute()]
public string regimeAlias {
get {
return this.regimeAliasField;
}
set {
if ((object.ReferenceEquals(this.regimeAliasField, value) != true)) {
this.regimeAliasField = value;
this.RaisePropertyChanged("regimeAlias");
}
}
}
The setter never gets hit. Is this because each element within the NewRegime object needs to raise PropertyChanged and if so is there an easy workaround without adding a further DTO layer to my code?
Edit3: with the post from your regimeAlias code. i have to say your binding should work. but of course if you wanna debug you have to set the breakpoint in your regimeAlias setter
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
this code means, you bind to a Public Property regimeAlias in your class tbRegime.
your setter for NewRegime will never hit because you dont bind to it.
so check your tbRegime class property setter for regimeAlias.
EDIT: the DataContext of the TextBox is of course an object with the Public Property NewRegime, but like i said if you use dot notation in your binding the last property is the one you bind to :)
EDIT: you dont have much ways to workaround:) if you let the binding like you did, you need a model with a public property regimeAlias and it should implement INotifyPropertyChanged.
if you wanna wrap the regimeAlias Property then you have the problem the you have to raise OnPropertyChanged("MyRegimeAlias") at the right point.
public string MyRegimeAlias
{
get { return _NewRegime.regimeAlias; }
set
{
_NewRegime.regimeAlias = value;
OnPropertyChanged("MyRegimeAlias");
}
}
xaml
<TextBox Text="{Binding MyRegimeAlias, Mode=TwoWay}"/>

Binding ListBox.SelectedItem to Property

This might be a duplicate question, but I'm unable to find a good answer. All the answers like Binding WinForms ListBox to object properties don't work on my WinForm. I'll explain.
I have a list of Firms that I show in a ListBox. I would like when the SelectedItem changes, that it updates a property on my model. So that I can read the Firms properties.
// the classes
public class Firm
{
public string Name { get; set; }
public int Id { get; set; }
// more properties ...
}
public class MyModel : INotifyPropertyChanged
{
private Firm _firm = new Firm();
public Firm Firm
{
get { return _firm; }
set
{
if (Equals(value, _firm)) return;
_firm = value;
OnPropertyChanged();
}
}
// more properties and OnPropertyChanged() ...
}
// the form
private MyModel Model;
public void MyForm(List<Firm> firms)
{
lstFirm.DataBindings.Add("SelectedItem", Model, "Firm",
true, DataSourceUpdateMode.OnPropertyChanged);
lstFirm.DisplayMember = "Name";
lstFirm.ValueMember = "Id";
lstFirm.DataSource = firms;
}
public void lstFirm_SelectedIndexChanged(object sender, EventArgs e)
{
// Do something with Model.Firm
}
The problem is that Model.Firm null is. Does anybody have an idea what I need to do to make a databinding between the ListBox and the Model? I bind other stuff on my WinForm (such as TextBoxes to String properties) and those work nicely.
From what I can see, your code never sets Model.Firm... Where's the constructor for MyModel? If you don't provide one, Model.Firm will stay null unless you explicitly set it. Here's an example constructor:
public MyModel(Firm firm)
{
_firm = firm;
}
Also, Equals() doesn't do what you think it does. Instead of if (Equals(value, _firm)) return;, use this: if (value == _firm) return;
Ok, so after a weekend of testing, I figured it out.
I was debuging in the SelectedIndexChanged event and didn't see the change in my Model.Firm just yet. But as the SelectedItemChanged event is only internal, I couldn't use that and that's where the databinding on SelectedItem applies the values to databound items.
Now the reason why the change isn't visible yet, is because the SelectedItemChanged is only fired after the SelectedIndexChanged is executed. So internally in the ListBox control, it probably looks like
this.SelectedIndex = value;
this.SelectedItem = FindItem(value);
this.SelectedIndexChanged(/*values*/);
this.SelectedItemChanged(/*values*/); // Apply databinding changes
So it's quite normal that you don't see the changes, before the change has occured. And I didn't know this, so I was kinda stumped why the SelectedItem (who was displaying the changed value) wasn't copied over to the databound model property.
So I didn't have to change anything major to get it all working. :)

How to create a DependancyProperty Collection with bindable Items in WPF

I need to create a DependancyProperty Collection
and somehow bind or be able to know when each item changes
It is not easy to explain this problem..
To declare a simple DependancyProperty we do that:
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register("SelectedColor", typeof(Color),
typeof(MyClass), new PropertyMetadata(Colors.Red));
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
And later on in XAML we can do that:
<MyClass SelectedColor="{Binding blah blah}"/>
If I add a simple callback on value change in the static declaration so instead of
just new PropertyMetadata(Colors.Red)
i'll have: new PropertyMetaData(Colors.Red,MyCallback)
Each time the value is changed I will be able to react to that change from code behind.
Ok, now what I want is something that will be written in XAML like that:
<MyClass>
<MyClass.SelectedColors>
<Color>{Binding blah1}</Color>
<Color>{Binding blah2}</Color>
<Color>{Binding blahN}</Color>
<MyClass.SelectedColors>
</MyClass>
However, I can't use multibinding as I want to know which color has
changed and multibinding gives me an aggregator tactic, meaning when
I done writing the multibinding converter I will get all the values
from all "blahs" at the same time and will not be able to know which
one was the one that changed..
I began writing something simple that look as follows:
public static readonly DependencyProperty SelectedColorsProperty =
DependencyProperty.Register("SelectedColors", typeof(List<Color>),
typeof(MyClass),
new PropertyMetadata(new List<Color>()));
public List<Color> SelectedColors
{
get { return (List<Color>)GetValue(SelectedColorsProperty); }
set { SetValue(SelectedColorsProperty, value); }
}
public MyClass()
{
foreach(var item in SelectedColors)
{
//dunno what goes here.. or how to bind the color change?
}
}
So now I can't create a callback that will handle each change of each item, right?
Yet that is exactly what I need, to be able to react to a change in each item..
However it does allow me to express the XAML syntax I need..
Any ideas? Did anyone bumped into that kind of problem before?
I noticed very short explanation in MSDN but i'm not sure if it helps me and how
to use it if can help, it is the last paragraph in the page that mentions
something about using a
Freezable<T>
http://msdn.microsoft.com/en-us/library/aa970563.aspx
I appreciate your help,
Thanks.
Update: A MultiBinding Solution can also be ok if it will allow to distinguish which of the values had the change (in our example which color changed).
The Markup in that case might look like that:
<MyClass>
<MyClass.SelectedColors>
<MultiBinding..
<Binding Path="FillColor" ElementName="MyRectWhichHasFillColorDProperty"/>
<Binding blah2 which is Color2 />
<Binding blah3 which is ColorN />
Wrap your Colors in a class that implements INotifyPropertyChanged and then setup a listener to the PropertyChanged event for each color in your constructor.
Wrapper class
public class ColorViewModel : INotifyPropertyChanged
{
private Color color;
public Color Color
{
get { return color; }
set
{
if (value != color)
{
color = value;
NotifyPropertyChanged("Color");
}
}
}
// INotifyPropertyChanged implementation goes here
}
Then in your event listener you will get the individual ColorViewModel that fired the property changed event.
I have had to solve a very similar problem before and I created an ObservableCollection of my ViewModel class so that I could subscribe to an collection changes and attach/detach my event listeners to the changed items.
I typed this from memory on my Surface so there are probably lots of typos and you should take the code sample as sudo code.
Hope this helps.
EDIT
Here is what your Xaml could look like:
<namespace:MyClass>
<namespace:MyClass.SelectedColors>
<namespace:ColorViewModel Color="{Binding SomeValue}" />
<namespace:ColorViewModel Color="{Binding OtherValue}" />
</namespace:myClass.SelectedColors>
</namespace:MyClass>
where Colors is a collection of ColorViewModel.
You will not be able to wire up your PropertyChanged listeners in your constructor since at the time of construction there are no ColorViewModels in Colors, so I would just make Colors an ObservableCollection and use the CollectionChanged event to wire up PropertyChanged listeners to any new additions to the collection.
You may just want to Use a SolidColorBrush instead of a ColorViewModel class as it has a Color property and a Changed event.

Using a custom attached property with a binding

I was looking at this question, but I don't understand how to actually USE the created AttachedProperty. The problem is trying to have a binding on the source of the WebBrowser control.
The code there looks like:
public static class WebBrowserUtility
{
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserUtility), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string) obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
WebBrowser browser = o as WebBrowser;
if (browser != null)
{
string uri = e.NewValue as string;
browser.Source = uri != null ? new Uri(uri) : null;
}
}
}
and
<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Width="300"
Height="200" />
The WebAddress, what is that exactly? This is my understanding (which is probably wrong):
There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like:
public String WebAddress
{
// get and set here? not sure
}
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
Even when I look at it, it doesn't seem right, but I can't find anything online to help me.
There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
You might want to read the MSDN article on attached properties.
It is rather simple: Dependency properties work with dictionaries in which controls are associated with their values for a property, this makes it quite easy to add something like attached properties which can extend a control.
In the RegisterAttached method of the attached property a PropertyChangedCallback is hooked up which will be executed if the value changes. Using a dependency property enables binding which is the point of doing this in the first place. All the property really does is call the relevant code to navigate the browser if the value changes.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like [...]
The binding references some public property or depedency property (not a field) called WebAddress inside the DataContext of the WebBrowser. For general information on data-binding see the Data Binding Overview.
So if you want to create a property which should be a binding source you either implement INotifyPropertyChanged or you create a DependencyProperty (they fire change notifications on their own and you normally do only create those on controls and UI-related classes)
Your property could look like this:
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private string _webAddress;
public string WebAddress
{
get { return _webAddress; }
set
{
if (value != _webAddress)
{
_webAddress = value;
NotifyPropertyChanged("WebAddress");
}
}
}
}
Here you have to raise the PropertyChanged event in the setter as you suspected. How to actually declare working bindings in XAML is a rather broad topic sp i would like to direct you to the aforementioned Data Binding Overview again which should explain that.
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
The event is fired to trigger the binding to update, this in turn changes the value of the attached property which in turn causes the PropertyChangedCallback to be executed which eventually navigates the browser.

Can't get ListBox and UpdateTarget to work

Here are the relevant parts of the XAML file:
xmlns:local="clr-namespace:BindingTest"
<ListBox x:Name="myList"
ItemsSource="{Binding Source={x:Static local:MyClass.Dic},
Path=Keys,
Mode=OneWay,
UpdateSourceTrigger=Explicit}">
</ListBox>
MyClass is a public static class and Dic is a static public property, a Dictionary.
At a certain point I add items to the Dictionary and would like the ListBox to reflect the changes.
This is the code I thought about using but it doesn't work:
BindingExpression binding;
binding = myList.GetBindingExpression(ListBox.ItemsSourceProperty);
binding.UpdateTarget();
This code instead works:
myList.ItemsSource = null;
myList.ItemsSource = MyClass.dic.Keys;
I would prefer to use UpdateTarget, but I can't get it to work.
What am I doing wrong?
Binding of items is handled rather differently than standard binding of DependencyPropertys in WPF (specifically, by ItemsControls).
I think you want something like the following:
var itemsView = CollectionViewSource.GetDefaultView(myListBox.ItemsSource);
itemsView.Refresh()
It is in fact the ICollectionView object that you want to refresh. This effectively is the object that manages the collection binding for you. See the MSDN page for more info.
Expanding on the previous answer by Noldorin, you can hook to the PropertyChanged event to make this happen automatically anytime your model is updated:
public partial class YourView {
public YourView () {
InitializeComponent();
ViewModel=YourDataObject;
ViewModel.PropertyChanged+=OnUpdateYourDataObject;
}
public YourDataObject? ViewModel {
get => DataContext as YourDataObject;
set => DataContext=value;
}
public void OnUpdateYourDataObject(object? sender, PropertyChangedEventArgs? e) =>
CollectionViewSource.GetDefaultView(YourListBox.ItemsSource).Refresh();
}
Replace YourDataObject and YourListBox with the correct values. This will automatically update your view whenever a property on your object changes. I'm not sure why this isn't handled automatically but I couldn't figure out the setting to make that work.

Categories