Need to act on a Silverlight RadioButton when it has been databound - silverlight

I'm creating a custom RadioButton that has some additional functionality. I need to somehow subscribe to an "OnDataBound" event - is there such a way to do this?
Thanks!

The simplest way i can think of:
1) Create a UserControl that encapsulates a RadioButton with a DependencyProperty that holds a CustomIsChecked object:
/// <summary>
/// Interaction logic for CustomRadioButton.xaml
/// </summary>
public partial class CustomRadioButton : UserControl
{
public CustomRadioButton()
{
InitializeComponent();
this.DataContext = this;
}
#region CustomIsChecked
public bool CustomIsChecked
{
get { return (bool)GetValue(CustomDataContextProperty); }
set { SetValue(CustomDataContextProperty, value); }
}
// Using a DependencyProperty as the backing store for CustomIsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CustomIsCheckedProperty =
DependencyProperty.Register("CustomIsChecked", typeof(bool), typeof(CustomRadioButton), new UIPropertyMetadata(new PropertyChangedCallback((dpo, dpce) =>
{
MessageBox.Show("IsChecked has changed!");
})));
#endregion
}
In the XAML part of the UserControl, bind the IsChecked of your RadioButton to the CustomIsChecked:
<UserControl x:Class="WpfApplication1.CustomRadioButton"
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="300" d:DesignWidth="300">
<RadioButton IsChecked="{Binding CustomIsChecked, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
</UserControl>
voila!

Related

DependencyProperty issues with respect to a UserControl and WPF Dialog

I've dumbed down the code as much as I could to try and get a working piece of code yet I'm still coming up short. Some advice would be appreciated.
I'm trying to get a DependencyProperty working, it's that simple and yet the data I'm setting on the main window isn't showing up in the user control.
In the MainWindow I'm setting the TextValue to "hi" in the xaml. TextValue is showing in the xaml up and compiling just fine so I'm pretty sure I have the DependencyProperty set right. Once the dialog is fully open I take a look in the debugger and my property TextValue is still null.
Am I missing setting the data context? Maybe I'm off base in what I'm looking to do.
Thanks for taking the time to figure out what I'm doing wrong.
My User Control is: UserControl1
Xaml:
<UserControl x:Class="WpfApplication1.UserControl1"
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"
Loaded="UserControl_Loaded"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
</Grid>
</UserControl>
UserControl1.xaml.cs is:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("TextValue", typeof(string), typeof(UserControl1));
private string _tv;
public string TextValue
{
get
{
return _tv;
}
set
{
_tv = value;
}
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
My calling window xaml is:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:usercontrols="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525"
Loaded='Window_Loaded'>
<Grid>
<usercontrols:UserControl1 x:Name="usercontroltest1" TextValue="hi"/>
</Grid>
</Window>
My calling window .cs is:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
The getter and setter of the "property wrapper" must call the GetValue and SetValue methods of the DependencyObject base class like shown below. Besides that, there is a naming convention that mandates that a dependency property's identifier field is named like the property plus a Property suffix. See Custom Dependency Properties for all the details.
public static readonly DependencyProperty TextValueProperty =
DependencyProperty.Register(
nameof(TextValue), typeof(string), typeof(UserControl1));
public string TextValue
{
get { return (string)GetValue(TextValueProperty); }
set { SetValue(TextValueProperty, value); }
}
In order to access a UserControl's dependency property in its own XAML, you would typically use a RelativeSource Binding like this:
<UserControl x:Class="WpfApplication1.UserControl1" ...>
<Grid>
<TextBlock Text="{Binding TextValue,
RelativeSource={RelativeSource AncstorType=UserControl}}" />
</Grid>
</UserControl>

Binding WPF UserControl to View Model and Code Behind

I'm attempting to understand the best way of wiring up a custom control to use Dependency Properties and a View Model. The Dependency Properties are implemented to expose properties that can be used in XAML to initialise the properties in the View Model. For example, in the code behind of a custom control I can define the following dependency property:
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
"MyProperty", typeof(string), typeof(MyControl), new PropertyMetadata(null));
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
where the View Model is defined as
public class MyControlViewModel : INotifyPropertyChanged
{
public MyControlViewModel()
{
_myProperty = "Default View Model string";
}
private string _myProperty;
public string MyProperty
{
get
{
return _myProperty;
}
set
{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
and the custom control binds to the View Model MyProperty as follows
<UserControl x:Class="MyProject.MyControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<UserControl.DataContext>
<local:MyControlViewModel/>
</UserControl.DataContext>
<Grid>
<TextBlock Text="{Binding MyProperty}"/>
</Grid>
</UserControl>
Now since I have defined the Dependency Property MyProperty in the custom control's code behind, I want to be able to use this to initialise MyProperty in the ViewModel. So something like this
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:MyControlView MyProperty="This was set in XAML"/>
</Grid>
</Window>
Running the above will display the string "Default View Model string" that was set in the View Model's constructor. How do I hook up the Dependency Property value so that it correctly initialises the string in the View Model? i.e. it should display "This was set in XAML".
UPDATE
I can set a property changed callback in the code behind and set the value in the View Model, i.e.
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(MyControlView), new PropertyMetadata("Default", OnMyPropertyChanged));
private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var view = d as MyControlView;
if (view != null)
{
var viewModel = view.DataContext as MyControlViewModel;
if (viewModel != null)
{
viewModel.MyProperty = e.NewValue as string;
}
}
}
Is this correct, or does it smell?
You have created dependency property that is nice. But, you have made tightly coupled by instiantiating your view model and using its property into code behind. Its wrong way.
You should inherit your class from "Control" and for binding value and create one DP like below in custom control class:
public static readonly DependencyProperty myValueProperty = DependencyProperty.Register(
"MyProperty", typeof(object), typeof(FieldControl), new FrameworkPropertyMetadata(null, myValueChanged));
Then,
private static void myValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var fc = dependencyObject as FieldControl;
if (fc != null)
{
fc.SetValue(BindingExpression1Key, fc.GetBindingExpression(ValueProperty));
fc.RaiseValueChangedEvent(dependencyPropertyChangedEventArgs);
}
}
Now, use it and bind your property like below:
<local:MyControlView MyProperty="{Binding MyProperty, Mode=TwoWay}"/>
You can update the ViewModel (INPC) property based on the View's Dependency Property (DP) using a Blend Behaviour. This solution avoids having to add a property changed callback in the code-behind:
<UserControl x:Class="MyProject.MyControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
xmlns:local="clr-namespace:MyProject"
x:Name="MyControl">
<UserControl.DataContext>
<local:MyControlViewModel/>
</UserControl.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ic:ChangePropertyAction
TargetObject="{Binding}"
PropertyName="MyProperty"
Value="{Binding ElementName=MyControl, Path=MyProperty}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<TextBlock Text="{Binding MyProperty}"/>
</Grid>
You will have to add references to the System.Windows.Interactivity and Microsoft.Expression.Interactivity.Core assemblies to your projects in order to use Blend Behaviours (note the i and ic namespaces that I added).
In this case I hook into the UserControl Loaded event with an EventTrigger that calls a ChangePropertyAction. By specifying {Binding} for the TargetObject, I'm informing the Behaviour to use the View Model from the bound DataContext. The PropertyName refers to the property on the View Model. The Value refers to the DP on the View. I gave your UserControl a name so that I could easily reference it when querying the value of MyProperty.
The benefit of doing it this way means that you could swap out your View Model in the DataContext (as long as the new one also has a MyProperty property) without having to update the code-behind.

Binding UserControl Dependency Property and MVVM

I have a MainWindow containing a UserControl, both implemented in MVVM-pattern.
The MainWindowVM has properties that I want to bind to properties in the UserControl1VM. But this doesn't work.
Here's some code (the viewmodels use some kind of mvvm-framework that implement the INotifyPropertyChanged in a ViewModelBase-class but that's hopefully no problem):
MainWindow.xaml:
<Window x:Class="DPandMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DPandMVVM"
Title="MainWindow" Height="300" Width="300">
<Grid>
<local:UserControl1 TextInControl="{Binding Text}" />
</Grid>
</Window>
CodeBehind MainWindow.xaml.cs:
using System.Windows;
namespace DPandMVVM
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowVM();
}
}
}
MainWindow-ViewModel MainWindowVM.cs:
namespace DPandMVVM
{
public class MainWindowVM : ViewModelBase
{
private string _text;
public string Text { get { return _text; } }
public MainWindowVM()
{
_text = "Text from MainWindowVM";
}
}
}
And here the UserControl1.xaml:
<UserControl x:Class="DPandMVVM.UserControl1"
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="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding TextInTextBlock}" />
</Grid>
</UserControl>
The Codebehind UserControl1.xaml.cs:
using System.Windows.Controls;
namespace DPandMVVM
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
DataContext = new UserControl1VM();
}
}
}
And the Viewmodel UserControl1VM.cs:
using System.Windows;
namespace DPandMVVM
{
public class UserControl1VM : DependencyObject
{
public UserControl1VM()
{
TextInControl = "TextfromUserControl1VM";
}
public string TextInControl
{
get { return (string)GetValue(TextInControlProperty); }
set { SetValue(TextInControlProperty, value); }
}
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register("TextInControl", typeof(string), typeof(UserControl1VM));
}
}
With this constellation the DP cannot be found in MainWindow.xaml.
What am I doing wrong?
First of all you want DependencyProperty TextInControl to be declared inside UserControl1 if you want to bind it from outside.
Move the declaration of DP inside of UserControl1.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string TextInControl
{
get { return (string)GetValue(TextInControlProperty); }
set { SetValue(TextInControlProperty, value); }
}
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register("TextInControl", typeof(string),
typeof(UserControl1));
}
Second you have externally set DataContext of UserControl to UserControl1VM,
public UserControl1()
{
InitializeComponent();
DataContext = new UserControl1VM(); <-- HERE (Remove this)
}
So WPF binding engine looking for property Text in UserControl1VM instead of MainWindowVM. Remove setting DataContext and update XAML of UserControl1 to this:
<UserControl x:Class="DPandMVVM.UserControl1"
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="300" d:DesignWidth="300"
x:Name="userControl1">
<Grid>
<TextBlock Text="{Binding TextInTextBlock, ElementName=userControl1}" />
</Grid>
</UserControl>
Bind DP using ElementName by setting x:Name on UserControl.
UPDATE
In case you want to have ViewModel intact for UserControl, you have to update binding in MainWindow. Explicitly tell WPF binding engine to look for property in MainWindow's DataContext using ElementName in binding like this:
<local:UserControl1 TextInControl="{Binding DataContext.Text,
ElementName=mainWindow}" />
For this you need to set x:Name="mainWindow" on window root level.
The XAML of your control right now reference the property TextInTextBlock via the DataContext which in turn "Points" to your main window's view model. Reference the data of the control and you are done (btw do not set the DataContext for that reason - the binding won't work any more):
<UserControl x:Class="DPandMVVM.UserControl1"
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="300" d:DesignWidth="300"
x:Name="self">
<Grid>
<TextBlock Text="{Binding TextInTextBlock, ElementName=self}" />
</Grid>
</UserControl>
This is how I do UserControls with MVVM and DP binding. It's similar to Rohit's answer but with some slight changes. Basically you need to set the Control's internal view model to be the DataContext of the root container within the UserControl rather than the UserControl itself, that way it will not interfere with DP bindings.
E.g.
UserControl XAML
<UserControl x:Class="DPandMVVM.UserControl1"
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="300" d:DesignWidth="300"
x:Name="userControl1">
<Grid x:Name="Root">
<TextBlock Text="{Binding TextFromVM}" />
</Grid>
UserControl Code-behind
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.ViewModel = new UserControlVM();
}
public UserControlVM ViewModel
{
get { return this.Root.DataContext as UserControlVM ; }
set { this.Root.DataContext = value; }
}
public string TextFromBinding
{
get { return (string)GetValue(TextFromBindingProperty); }
set { SetValue(TextFromBindingProperty, value); }
}
public static readonly DependencyProperty TextFromBindingProperty =
DependencyProperty.Register("TextFromBinding", typeof(string), typeof(UserControl1), new FrameworkPropertyMetadata(null, OnTextBindingChanged));
private static void OnTextBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uc = d as UserControl1;
uc.ViewModel.TextFromVM = e.NewValue as string;
}
}
This means that the control derives it's values from the Root element DataContext which is our ViewModel but the ViewModel can be updated via a DP binding from outside the control (in your case a binding to the parent Window's ViewModel, see below)
Window XAML
<Window x:Class="DPandMVVM.Window1"
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:DPandMVVM"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="window1">
<Grid x:Name="Root">
<local:userControl1 TextFromBinding="{Binding TextFromWindowVM}" />
</Grid>
I have a method that I believe is a lot simpler, and probably more true to MVVM.
In the main window XAML:
<myNameSpace:myUserControl DataContext="{Binding Status}"/>
In your main view model (the data context of the main window:
public myUserControlViewModel Status { set; get; }
now you can in the constructor (or whenever you want to instantiate it):
Status = new myUserControlViewModel();
then if you want to set the text property:
Status.Text = "foo";
and make sure you have the binding setup to a property named Text inside your myUserControlViewModel class:
<TextBox Text="{Binding Text}"/>
and make sure the property fires PropertyChanged, of-course.
Plus, if you use Resharper. You can create a Design instance of the UserControl in your XAML so that it can link the bindings and not tell you that the property is never used by doing this:
<UserControl x:Class="myNameSpace.myUserControl"
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:myNameSpace="clr-namespace:myNameSpace"
d:DataContext="{d:DesignInstance myNameSpace.myUserControl}"
mc:Ignorable="d" ...>
This part:
xmlns:myNameSpace="clr-namespace:myNameSpace"
d:DataContext="{d:DesignInstance myNameSpace.myUserControl}"
Here's a possible working solution for you. However, I've noted in a comment above that this will work in code and perhaps (like my situation) will show up as an error (Object Not Found) in the designer:
<local:UserControl1 TextInControl="{Binding DataContext.Text,
Source={x:Reference <<Your control that contains the DataContext here>>}}" />
I'd rather to have a cleaner solution, though, without any designer errors. I wish to find out how to properly bind a dependency property in a user control to a value coming from the window it's contained in. What I'm finding is that whatever I try to do (short of what I showed above), such as using ElementName and/or AncestorType/Level, etc., the debugger complains that it can't find the source and shows that it's looking for the source inside the context of the user control! It's like I can't break out of the user control context when doing Binding logic in the use of that control (other than that "designer-breaking" solution above).
UPDATE:
I noticed that this might not work for you as your situation might force a problem I just noticed if I change my own source to reference the window instead of a control that has the data context. If I reference the window then I end up with a cyclical redundancy. Perhaps you'll figure out a way to use the Source version of the binding that will work okay for you.
I must also add that my situation is probably a bit more complex since my usercontrol is used in the context of a popup.

Can't bind to my dependency property

I have a dependency property in a class which inherits from Canvas, like so:
public partial class HueVisualizer : Canvas
{
public HueVisualizer()
{
InitializeComponent();
}
public decimal InnerHue
{
get { return (decimal)GetValue(HueProperty); }
set { SetValue(HueProperty, value); }
}
// Using a DependencyProperty as the backing store for InnerHue,Saturation and Luminance. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HueProperty =
DependencyProperty.Register("InnerHue", typeof(decimal), typeof(LuminanceVisualizer), new FrameworkPropertyMetadata((decimal)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
I'm trying to bind to it in Xaml like so:
<UserControl x:Class="Project1.UserControl1"
x:Name="TheControl"
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:project1="clr-namespace:Project1"
mc:Ignorable="d"
d:DesignHeight="120" d:DesignWidth="300">
...
<Grid Grid.Row="0" x:Name="HueGrid">
<project1:HueVisualizer x:Name="HueVisual"
InnerHue ="{Binding ElementName=TheControl, Path=Hue, Mode=TwoWay}"
Height="20"
Width="{Binding ElementName=TheControl, Path=Width}"/>
</Grid>
<UserControl />
For completeness, the properties I'm trying to bind from:
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public decimal Hue
{
get { return (decimal)GetValue(HueProperty); }
set { SetValue(HueProperty, value); }
}
...
// Using a DependencyProperty as the backing store for Hue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HueProperty =
DependencyProperty.Register("Hue", typeof(decimal), typeof(UserControl1), new
FrameworkPropertyMetadata((decimal)0));
...
}
However, when I try to run/debug the project, I get an exception on InitializeComponent() of UserControl1:
A 'Binding' cannot be set on the 'InnerHue' property of type 'HueVisualizer'. A
'Binding' can only be set on a DependencyProperty of a DependencyObject.
No matter how many times I look at examples, it seems to me that InnerHue should be a valid dependency property. I also double checked to make sure that Canvas is a DependencyObject (if it weren't, GetValue and SetValue should throw a compiler error). What in the world am I doing incorrectly? As I am relatively new to WPF, I can't help but feel that I'm missing something obvious.
You gave the wrong owner type for your DependencyProperty
you wrote LuminanceVisualizer and it should be HueVisualizer .
public static readonly DependencyProperty HueProperty =
DependencyProperty.Register("InnerHue", typeof(decimal),
typeof(LuminanceVisualizer), // Replace with HueVisualizer
new FrameworkPropertyMetadata((decimal)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault))

WPF - property change notification from UserControl

I have two UserControls (uc1 and uc2) loading into a third UserControl (shell). Shell has two properties, uc1 and uc2, of type UserControl1 and UserControl2, and each have a DependencyProperty registered to their own classes called IsDirty:
public static readonly DependencyProperty IsDirtyProperty = DependencyProperty.Register("IsDirty", typeof (bool), typeof (UserControl1));
public bool IsDirty
{
get { return (bool) GetValue(IsDirtyProperty); }
set { SetValue(IsDirtyProperty, value); }
}
(same code for UserControl2)
Shell has TextBlocks bound to the IsDirty properties:
<TextBlock Text="{Binding ElementName=shell, Path=Uc1.IsDirty}"/>
<TextBlock Text="{Binding ElementName=shell, Path=Uc2.IsDirty}"/>
When I change the values of IsDirty in uc1 and uc2, Shell never gets notified. What am I missing? UserControl is descendant of DependencyObject...
The same behavior occurs if I have regular properties notifying changes via INotifyPropertyChanged.
If I raise a routed event from uc1 and uc2, bubbling up to Shell, then I can catch the Dirty value and everything works, but I shouldn't have to do that, should I?
Thanks
Edit: The answer is to raise property changed event on the Uc1 and Uc2 properties or make them DPs.
I tried reproducing your problem using a simple setup, and it works fine for me. I'm not sure though if this setup is correct enough to replicate your situation. Anyway, I'm posting it just in case. It might be helpful:
XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
x:Name="shell"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Click="Button_Click">Click</Button>
<TextBlock Text="{Binding ElementName=shell, Path=Uc1.IsDirty}"/>
</StackPanel>
</Window>
Code-Behind:
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MyUserControl uc1 = new MyUserControl();
public MyUserControl Uc1
{
get { return this.uc1; }
}
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.uc1.IsDirty = !this.uc1.IsDirty;
}
}
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
}
public bool IsDirty
{
get { return (bool)GetValue(IsDirtyProperty); }
set { SetValue(IsDirtyProperty, value); }
}
// Using a DependencyProperty as the backing store for IsDirty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsDirtyProperty =
DependencyProperty.Register("IsDirty", typeof(bool), typeof(UserControl), new UIPropertyMetadata(false));
}
}
Karmicpuppet's answer works well. However it didn't solve my problem because Shell is also of type UserControl. For it to work I needed to raise the property changed on Uc1 and Uc2. When I declared them as DependencyProperties all worked as expected. Duh!

Resources