WPF dependency property - Get ignored - c#

I have the following class:
public class Person:DependencyObject
{
public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(String), typeof(Person));
public string Name
{
get
{
string result = (string)GetValue(NameProperty);
return result;
}
set
{
SetValue(NameProperty, value);
}
}
}
And the following Window:
<Window x:Class="BindingSelf.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>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding Name}"></TextBox>
</Grid>
</Window>
The code behind for Window is:
public partial class MainWindow : Window
{
Person p = null;
public MainWindow()
{
InitializeComponent();
p = new Person();
p.Name = "Test1";
this.DataContext = p;
}
}
TextBox is bound to Name and it's value ("Test1") correctly shows when I run the application. Now here's my question, if I set a break point in the Get part of the Name property it is completely ignored. I've done a few tests and even If I return empty "Test1" still shows, could somebody explain what's happening?
Thanks

Explanation from MSDN link is self explanatory:
The current WPF implementation of its XAML processor is inherently
dependency property aware. The WPF XAML processor uses property system
methods for dependency properties when loading binary XAML and
processing attributes that are dependency properties. This effectively
bypasses the property wrappers. When you implement custom dependency
properties, you must account for this behavior and should avoid
placing any other code in your property wrapper other than the
property system methods GetValue and SetValue.

Related

Synchronizing Dependency Properties (View) with Properties (ViewModel)

Does anybody know how I can synchronize my properties, which are in a ViewModel, with my Dependency Properties, which are in the View?
I am trying to make a UserControl, which will then be hosted by a WPF-Window (MainWindow.xaml). The UserControl has an own ViewModel which contains ICommands and properties.
The problem is, that I also have to return certain properties to the MainWindow(.xaml) and also set them.
Currently my classes are looking like that:
MainWindow.xaml
<TextBox Name="tbInput" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Row="0"></TextBox>
<local:View x:Name="appEntryView" Pfad="{Binding ElementName=tbInput, Path=Text}" Grid.Row="1" Margin="10"/>
View.xaml
<UserControl x:Class="DependencyProperties.Test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DependencyProperties_Intro"
x:Name="obj"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding ElementName=obj, Path=Pfad}"/>
</Grid>
View.xaml.cs
public partial class View: UserControl, INotifyPropertyChanged
{
public String Pfad
{
get { return (String)GetValue(PfadProperty); }
set { SetValue(PfadProperty, value); OnNotifyPropertyChanged("Pfad"); }
}
// Using a DependencyProperty as the backing store for Path. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PfadProperty =
DependencyProperty.Register("Pfad", typeof(String), typeof(GraphSharpTest), new PropertyMetadata(default(string)));
public View()
{
InitializeComponent();
this.DataContext = new ViewModel();
var name = "Pfad";
var binding = new Binding(name) { Mode = BindingMode.TwoWay };
this.SetBinding(PfadProperty, binding);
}
}
ViewModel.cs
public class ViewModel: INotifyPropertyChanged
{
private String m_Pfad;
public String Pfad
{
get { return m_Pfad; }
set { m_Pfad = value; OnNotifyPropertyChanged("Pfad"); }
}
public void OnNotifyPropertyChanged(String info)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info));
}
public event PropertyChangedEventHandler PropertyChanged;
}
The dependency property works fine, but the setter method of "Pfad" in the ViewModel never gets called at all.
Thanks in advance!
Raising PropertyChanged in the CLR properties of dependency properties is a common mistake. You should not place any code there as it is not used by bindings at all. They exist merely for setting the property once in code or XAML, thus you also will not hit any breakpoints you set there.
var name = "Pfad";
var binding = new Binding(name) { Mode = BindingMode.TwoWay };
this.SetBinding(PfadProperty, binding);
I take it you want to forward the value to your view-model. This is not going to work as you can only bind the property once. Right now you also bind the property here:
<local:View x:Name="appEntryView" Pfad="{Binding ElementName=tbInput, Path=Text}" Grid.Row="1" Margin="10"/>
You could subscribe to the dependency property changes using the respective meta data when registering it, providing a callback, there you could set the value in the view-model.
The thing is: The view-model is private to the View, there really is no point in doing this synchronization if no-one has access to the data. You probably want the property to be either settable from the outside, treating the UserControl more like a control, discarding the view-model, or you want the view-model to be passed from outside as DataContext, and the view binds directly to it.
You need to be careful with explicitly setting the DataContext of UserControls in their definition, as it can obfuscate what is happening and lead to bindings unexpectedly breaking. If you want to set properties on the UserControl instance i would recommend avoiding it.

How to set a WPF usercontrol property from XAML?

I'm trying to set the fill property of several instances of the same usercontrol from XAML in order to distinguish them. I'm using a dependency property in the C# codebehind of the control and referring to that in the XAML when I instantiate the control. Here's a simplified example of what I've tried, first the XAML of the user control:
<UserControl x:Class="RectangleFillUserControlTest.RectangleFillTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="150">
<Grid>
<Rectangle x:Name="rect" HorizontalAlignment="Left" Height="50" Stroke="Black" VerticalAlignment="Top" Width="150"/>
</Grid>
</UserControl>
Now the codebehind:
namespace RectangleFillUserControlTest
{
public partial class RectangleFillTest : UserControl
{
SolidColorBrush fillBrush;
public static readonly DependencyProperty FillColourProperty = DependencyProperty.Register
("FillColour", typeof(string), typeof(RectangleFillTest), new PropertyMetadata(string.Empty));
public string FillColour
{
get { return (string)GetValue(FillColourProperty); }
set
{
SetValue(FillColourProperty, value);
if (value == "red") fillBrush = new SolidColorBrush(Colors.Red);
else fillBrush = new SolidColorBrush(Colors.Green);
rect.Fill = fillBrush;
}
}
public RectangleFillTest()
{
InitializeComponent();
}
}
}
I instantiate the control in the main window and try to set the fill colour to red:
<Window x:Class="RectangleFillUserControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RectangleFillUserControlTest"
Title="MainWindow" Height="350" Width="525">
<Grid Background="#FF1D2CC3">
<local:RectangleFillTest FillColour="red"/>
</Grid>
</Window>
But the rectangle remains unfilled, even when I run the project. Can anyone help please?
Cheers,
Tim
There are two things wrong with your dependency property.
First, its type should be Brush, not string, because that is the type used by properties of WPF controls like Shape.Fill or Control.Background. WPF provides automatic type conversion from strings like "Red" or "#FFFF0000" in XAML to type Brush.
Second, you should not have anything else than a call to SetValue in the setter method of the CLR wrapper. The reason is explained in the XAML Loading and Dependency Properties article on MSDN:
Because the current WPF implementation of the XAML processor behavior
for property setting bypasses the wrappers entirely, you should not
put any additional logic into the set definitions of the wrapper for
your custom dependency property. If you put such logic in the set
definition, then the logic will not be executed when the property is
set in XAML rather than in code.
So your dependency property declaration should look like this:
public static readonly DependencyProperty FillBrushProperty =
DependencyProperty.Register(
"FillBrush", typeof(Brush), typeof(RectangleFillTest));
public Brush FillBrush
{
get { return (Brush)GetValue(FillBrushProperty); }
set { SetValue(FillBrushProperty, value); }
}
To react to property changes, you would now register a PropertyChangedCallback with property metadata. But you don't need to do that here, because you could simply bind the property in the UserControl's XAML like this:
<Rectangle Fill="{Binding FillBrush,
RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" ... />
I will explain why is is not working and how to solve.
1.- A Dependency Property is only called when the usercontrol has that dependency property in the visual tree.
In case you want to do in that way, you need to add for instance :
new PropertyMetadata(string.Empty, ValueChanged));
and there change the value:
public static readonly DependencyProperty FillColourProperty = DependencyProperty.Register
("FillColour", typeof(string), typeof(RectangleFillTest), new PropertyMetadata(string.Empty, ValueChanged));
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as RectangleFillTest;
var fillBrush = new SolidColorBrush();
if (control.FillColour == "red")
fillBrush = new SolidColorBrush(Colors.Red);
else
fillBrush = new SolidColorBrush(Colors.Green);
control.rect.Fill = fillBrush;
}
public string FillColour
{
get
{
return (string)GetValue(FillColourProperty);
}
set
{
SetValue(FillColourProperty, value);
}
}
That is explicit for your logic, in case you need a more generic code for any color, etc using binding the property to the rectangle, just tell me.
You need to bind your Dependency Property to the Fill property of your Rectangle in the xaml of your UserControl. You'll have Something like this :
<Rectangle x:Name="rect" Fill="{Binding FillColour, RelativeSource={RelativeSource FindAncestor, AncestorType=RectangleFillTest}}" HorizontalAlignment="Left" Height="50" Stroke="Black" VerticalAlignment="Top" Width="150"/>
Also, in your dependency property, it's type should be Brush, and not String.

DesignInstance does not work in VS2013

Visual Studio does not show design time data with DesignInstance attribute. I have checked DesignInstance with/without MVVM Light. I have spend a lot of time to fix the issue (checked similar queestions on StackOverflow too) but DesignInstance simply does not work.
Project:
SearchIdView.
SearchIdViewModel - real View Model.
DesignSearchIdViewModel - inherits from SearchIdViewModel and contains design time data (properties are assigned in constructor).
Environment:
VS2013 SP3
Net 4.0
MvvmLight 5.0.2.0
SearchIdView.xaml
<Window x:Class="App1.View.SearchIdView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
xmlns:design="clr-namespace:App1.Design"
mc:Ignorable="d ignore"
DataContext="{Binding SearchId, Source={StaticResource Locator}}"
d:DataContext="{d:DesignInstance d:Type=design:DesignSearchIdViewModel,IsDesignTimeCreatable=True}"
>
<Grid>
<TextBlock Text="{Binding Test}" />
</Grid>
SearchIdViewModel.cs
Property from SearchIdViewModel
public const string TestPropertyName = "Test";
private string _test;
public string Test
{
get
{
return _test;
}
set
{
Set(TestPropertyName, ref _test, value);
}
}
Do you have any idea why DesignInstance does not work in this case?
Workaround
remove d:DataContext from view
add interface ISearchIdViewModel (it is empty)
SearchIdViewModel inherits from ISearchIdViewModel
change ViewModelLocator (below)
ViewModelLocator.cs
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<ISearchIdViewModel,Design.DesignSearchIdViewModel>();
}
else
{
SimpleIoc.Default.Register<ISearchIdViewModel, SearchIdViewModel>();
}
}
public SearchIdViewModel SearchId
{
get { return (SearchIdViewModel) ServiceLocator.Current.GetInstance<ISearchIdViewModel>(); }
}
}
Your d:DesignInstance declaration is malformed. You specify the property name d:Type instead of Type, so the property is not assigned correctly. Either replace d:Type with Type, or leave the property name off entirely and let it be inferred as the default property.
d:DataContext="{d:DesignInstance d:Type=design:DesignSearchIdViewModel,
IsDesignTimeCreatable=True}"
Should become:
d:DataContext="{d:DesignInstance Type=design:DesignSearchIdViewModel,
IsDesignTimeCreatable=True}"
Or, alternatively:
d:DataContext="{d:DesignInstance design:DesignSearchIdViewModel,
IsDesignTimeCreatable=True}"
(line wrapping added for readability)
Another cause that might make d:DesignInstance not to work is that all data must be properties not just public variables of mock class! I know that it was not your problem, but it should be checked if for someone it does not work.
Will not work with:
public class MockFile
{
public FilePRJO FilePRJO = new FilePRJO();
}
But it will work with:
public class MockFile
{
public FilePRJO _filePRJO = new FilePRJO();
public FilePRJO FilePRJO
{
get
{
return _filePRJO;
}
}
}

Bind SelectedValue of UserControl to ViewModel

In my solution; I have two projects: One is a WPF UserControl Library, and the other is a WPF Application.
The usercontrol is pretty straightforward; it's a label and a combo box that will show the installed printers.
In the WPF application; I want to use this usercontrol. The selected value will be stored in user settings.
The problem I'm having is that I can't seem to get the proper binding to work. What I need to happen is to be able to set the SelectedValue of the UserControl when the MainWindow loads; as well as access the SelectedValue of the UserControl when I go to save my settings.
My code is below, could someone point me in the right direction?
PrintQueue user control:
<UserControl x:Class="WpfControls.PrintQueue"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:wpfControls="clr-namespace:WpfControls"
mc:Ignorable="d">
<UserControl.DataContext>
<wpfControls:PrintQueueViewModel/>
</UserControl.DataContext>
<Grid>
<StackPanel Orientation="Horizontal">
<Label Content="Selected Printer:"></Label>
<ComboBox ItemsSource="{Binding Path=PrintQueues, Mode=OneWay}" DisplayMemberPath="Name" SelectedValuePath="Name" Width="200" SelectedValue="{Binding Path=SelectedPrinterName, Mode=TwoWay}"></ComboBox>
</StackPanel>
</Grid>
</UserControl>
Print Queue Codebehind:
public partial class PrintQueue : UserControl
{
public static readonly DependencyProperty CurrentPrinterNameProperty =
DependencyProperty.Register("CurrentPrinterName", typeof (string), typeof (PrintQueue), new PropertyMetadata(default(string)));
public string CurrentPrinterName
{
get { return (DataContext as PrintQueueViewModel).SelectedPrinterName; }
set { (DataContext as PrintQueueViewModel).SelectedPrinterName = value; }
}
public PrintQueue()
{
InitializeComponent();
DataContext = new PrintQueueViewModel();
}
}
PrintQueue View Model:
public class PrintQueueViewModel : ViewModelBase
{
private ObservableCollection<System.Printing.PrintQueue> printQueues;
public ObservableCollection<System.Printing.PrintQueue> PrintQueues
{
get { return printQueues; }
set
{
printQueues = value;
NotifyPropertyChanged(() => PrintQueues);
}
}
private string selectedPrinterName;
public string SelectedPrinterName
{
get { return selectedPrinterName; }
set
{
selectedPrinterName = value;
NotifyPropertyChanged(() => SelectedPrinterName);
}
}
public PrintQueueViewModel()
{
PrintQueues = GetPrintQueues();
}
private static ObservableCollection<System.Printing.PrintQueue> GetPrintQueues()
{
var ps = new PrintServer();
return new ObservableCollection<System.Printing.PrintQueue>(ps.GetPrintQueues(new[]
{
EnumeratedPrintQueueTypes.Local,
EnumeratedPrintQueueTypes.Connections
}));
}
}
Main Window:
<Window x:Class="WPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfControls="clr-namespace:WpfControls;assembly=WpfControls" xmlns:wpfApp="clr-namespace:WPFApp"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<wpfApp:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<wpfControls:PrintQueue CurrentPrinterName="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.PrinterName, Mode=TwoWay}"></wpfControls:PrintQueue>
</StackPanel>
</Grid>
</Window>
Main Window View Model:
public class MainWindowViewModel : ViewModelBase
{
private string printerName;
public string PrinterName
{
get { return printerName; }
set
{
printerName = value;
NotifyPropertyChanged(() => PrinterName);
}
}
public MainWindowViewModel()
{
PrinterName = "Lexmark T656 PS3";
}
}
Controls in a library need to expose DependencyProperties that you can bind to in your view. Just like WPF's TextBox exposes a Text property.
Your PrintQueue control doesn't expose anything, and instead keeps all its state in a viewmodel that nothing outside can access. Your MainWindowViewModel has no way of getting at the stuff inside PrintQueueViewModel.
You need to expose SelectedPrinterName as a DependencyProperty in the code behind of your PrintQueue xaml. Then in MainWindow.xaml you can bind it to MainWindowViewModel.PrinterName.
If you want to user ViewModels all the way through instead, then MainWindowViewModel should be creating PrintQueueViewModel itself so it can access the properties within.
As per your update / comment:
Unfortunately DependencyProperties don't work like that. The getters/setters aren't even used most of the time, and they should ONLY update the property itself. You're sort of halfway between two worlds at the moment.
If I were in your position, and assuming you can change the library so PrintQueue.xaml doesn't have a hardcoded VM instance in the view, I would just create the PrintQueueViewModel yourself. That's how MVVM is supposed to work:
ViewModel:
public class MainWindowViewModel : ViewModelBase
{
public PrintQueueViewModel PrintQueue { get; private set; }
public MainWindowViewModel()
{
PrintQueue = new PrintQueueViewModel();
PrintQueue.SelectedPrinterName = "Lexmark T656 PS3";
}
}
View:
<Window x:Class="WPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfControls="clr-namespace:WpfControls;assembly=WpfControls" xmlns:wpfApp="clr-namespace:WPFApp"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<wpfApp:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<wpfControls:PrintQueue DataContext="{Binding PrintQueue}"/>
</StackPanel>
</Grid>
</Window>
Again though, control libraries generally don't have view models, and expose their state via dependency properties since they're designed to be used in XAML.
Component libraries may expose view models, but in that case they wouldn't hard code the view model in the view.
Did you write the library? If not, how did the author expect people to use it?
I think with this small changes everything should work
<ComboBox ItemsSource="{Binding Path=PrintQueues, Mode=OneWay}" DisplayMemberPath="Name" Width="200" SelectedItem="{Binding Path=SelectedPrinter, Mode=TwoWay}"></ComboBox>
private System.Printing.PrintQueue selectedPrinter;
public System.Printing.PrintQueue SelectedPrinter
{
get { return selectedPrinter; }
set
{
selectedPrinter = value;
NotifyPropertyChanged(() => SelectedPrinter);
}
}
Now from the main window you can modify SelectedPrinter on the viewmodel and the change should be reflected on the view
(PrintQueue.DataContext as PrintQueueViewModel).SelectedPrinter = ...
I tried your code and your bindings of the PrintQueueView to the corresponding view model work fine. Your problem is that the MainWindowViewModel does not know about the PrintQueueViewModel and thus cannot retrieve the value of the selected printer when the main window closes (I guess that is the scenario you want to implement).
The quickest solution to your problem would be to do the following steps:
In MainWindow.xaml, give PrintQueue a Name so you can access it in the code behind
In MainWindow.xaml.cs, override the OnClosing method. In it you can retrieve the view model as follows: var viewModel = (PrintQueueViewModel)PrintQueue.DataContext;. After that you can retrieve the selected value and save it or whatever.
In the MainWindow constructor after InitializeComponent, you can retrieve your saved value from a file and set it on the PrintQueueViewModel by retrieving it the same way as in the previous step.
Whole code in MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Retrieve your selected printer here; in this case, I just set it directly
var selectedPrinter = "Lexmark T656 PS3";
var viewModel = (PrintQueueViewModel)PrintQueue.DataContext;
viewModel.SelectedPrinterName = selectedPrinter;
}
protected override void OnClosing(CancelEventArgs e)
{
var viewModel = (PrintQueueViewModel)PrintQueue.DataContext;
var selectedPrinterName = viewModel.SelectedPrinterName;
// Save the name of the selected printer here
base.OnClosing(e);
}
}
Please remember that the major point of view models is the ability to unit-test GUI logic and to disconnect GUI appearance and logic. Your view models should not be able to retrieve all the possible printers of your system but should obtain these values by e.g. Dependency Injection. I would advise you to read about SOLID programming.

How to programmatically set SelectedItem of a data-bound WPF ComboBox?

Question: Can anyone please provide a full code example that shows how one does programmatically change the SelectedItem of a data-bound WPF ComboBox without using MyComboBox.SelectedIndex?
Code sample: Here is what I currently have.
XAML:
<Window x:Class="Wpf.ComboBoxDemo.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>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ComboBox Name="MyComboBox" DisplayMemberPath="LastName" SelectedIndex="0"/>
</Grid>
</Window>
Code-behind:
using System.Collections.ObjectModel;
using System.Windows;
namespace Wpf.ComboBoxDemo
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<Person> myPersonList = new ObservableCollection<Person>();
Person personJobs = new Person("Steve", "Jobs");
Person personGates = new Person("Bill", "Gates");
myPersonList.Add(personJobs);
myPersonList.Add(personGates);
MyComboBox.ItemsSource = myPersonList;
// How do I programmatically select the second Person, i.e. "Gates"?
// The best pratice must be to somehow to set something like IsCurrentlySelected on the model, so the view update automatically. But how?
MyComboBox.SelectedIndex = 1; // This works, but is there no way without using the index?
}
private class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
}
}
Similar questions: I have of course searched the Internet first, but found nothing that helped me.
Changing the SelectedItem of a enum-bound combobox inside ViewModel (MSDN)
Programmatically set ComboBox SelectedItem in WPF (3.5sp1) (Stack Overflow)
At the top of my head (I might be wrong), make the window implement INotifyPropertyChanged and add the event:
namespace Wpf.ComboBoxDemo
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
Then add a property for the currently selected item which notifies on changes:
private Person _selected;
public Person MySelected
{
get { return _selected; }
set
{
if (value != _selected)
{
_selected = value;
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs("MySelected"));
}
}
}
}
Now bind the combobox (the binding could be more advanced here using FindAncestor but sometimes to keep things simple I put the datacontext in code behind):
XAML:
<ComboBox
Name="MyComboBox"
DisplayMemberPath="LastName"
SelectedItem="{Binding MySelected}" />
Code behind:
public MainWindow()
{
InitializeComponent();
// ...
// this will cause the "MySelected" binding to target the correct property on this object
MyComboBox.DataContext = this;
}
I think it goes something like that. I cant test it right now but hopefully it will nudge you in the right direction.
Edit: If you want to try the "other way" of binding heres how. Expand the SelectedItem binding to look like this:
<ComboBox
Name="MyComboBox"
DisplayMemberPath="LastName"
SelectedItem="{Binding MySelected,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}" />
You can now skip setting the DataContext in code behind:
public MainWindow()
{
InitializeComponent();
// ...
// this will cause the "MySelected" binding to target the correct property on this object
//MyComboBox.DataContext = this;
}
This is because that FindAncestor mode makes the ComboBox itself find the object to which property it should bind, rather than you specifically stating.
The current hot topic here at the office is which of these two ways are the best. To me its just more XAML and less code behind (or the other way around), just use the method that places the code where youre comfortable to work. I think there are some scenarios where the latter is preferred (like when you include data binding controls inside other controls), but Im just dabbling so I havent really figured those parts out yet.

Categories

Resources