FreezableCollection not providing change notification when sub properties change - c#

I have a FreezableCollection for which I want to monitor the changes to sub properties. Here is a subsection of the code:
public class FieldHeading : DependencyObject
{
public static readonly DependencyProperty LayoutProperty = DependencyProperty.Register("Layout", typeof(FieldHeadingLayout), typeof(FieldHeading),
new FrameworkPropertyMetadata(FieldHeadingLayout.Above,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public FieldHeadingLayout Layout
{
get { return (FieldHeadingLayout) GetValue(LayoutProperty); }
set { SetValue(LayoutProperty, value); }
}
}
public class FieldPanel : FrameworkElement
{
private static readonly DependencyProperty FieldHeadingProperty = DependencyProperty.Register("FieldHeading", typeof(FreezableCollection<FieldHeading>), typeof(FieldPanel),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsParentMeasure |
FrameworkPropertyMetadataOptions.AffectsRender, HeadingChanged));
private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Hello");
}
public FreezableCollection<FieldHeading> FieldHeadings
{
get
{ return (FreezableCollection<FieldHeading>) GetValue(FieldHeadingProperty); }
set { SetValue(FieldHeadingProperty, value);}
}
public FieldPanel()
{
AddVisual(_contentVisual = new DrawingVisual());
FieldHeadings = new FreezableCollection<FieldHeading>();
}
}
Then we assign a new value to Layout for one of the FieldHeadings, no change notification is generated. Obviously I'm missing something important. HeadingChanged is never called.
The MSDN help on FreezableCollection, which can be found here: FreezableCollection, states:
Event changed... Occurs when the Freezable or an object it contains is modified. (Inherited from Freezable.)
Thanks in advance for any help.
~ Cameron

Actually, you can do what you're trying to do. This is exactly why FreezableCollection<T> exists! All you need to do is change FieldHeading to derive from Freezable instead of DependencyObject and changes to items in the collection will give the same change notification as if the entire item had been replaced.
This is an incredibly useful and little-known WPF feature.
From Charles Petzold himself,
These freezable collection classes fire change notifications whenever items are added to or removed from the collection, of course, but also when a dependency property of any item within the collection changes. This is an extremely powerful mechanism.
Here is a small sample that demonstrates how to use FreezableCollection<>. I made a new WPF project in Visual Studio. Here is the XAML for MainWindow.xaml.cs and its C# code behind:
<Window x:Class="FreezableCollection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FreezableCollection"
x:Name="Root"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyFreezable}">
<CheckBox IsChecked="{Binding IsNice}" Content="Check me!"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding ElementName=Root, Path=MyFreezables}" />
</Grid>
</Window>
using System.Windows;
namespace FreezableCollection
{
public partial class MainWindow : Window
{
public static readonly DependencyProperty MyFreezablesProperty =
DependencyProperty.Register("MyFreezables", typeof(MyFreezableCollection), typeof(MainWindow), new FrameworkPropertyMetadata(null, FreezablesChangedCallback));
public MyFreezableCollection MyFreezables
{
get => (MyFreezableCollection)GetValue(MyFreezablesProperty);
set => SetValue(MyFreezablesProperty, value);
}
public MainWindow()
{
InitializeComponent();
MyFreezables = new MyFreezableCollection { new MyFreezable() };
}
private static void FreezablesChangedCallback(object sender, DependencyPropertyChangedEventArgs args)
{
MessageBox.Show("Changed!");
}
}
public class MyFreezableCollection : FreezableCollection<MyFreezable>
{
protected override Freezable CreateInstanceCore() => new MyFreezableCollection();
}
public class MyFreezable : Freezable
{
public static readonly DependencyProperty IsNiceProperty =
DependencyProperty.Register("IsNice", typeof(bool), typeof(MyFreezable), new PropertyMetadata(false));
public bool IsNice
{
get => (bool)GetValue(IsNiceProperty);
set => SetValue(IsNiceProperty, value);
}
protected override Freezable CreateInstanceCore() => new MyFreezable();
}
}
The sample displays a list containing a single CheckBox. Click on it to toggle the IsNice property on an item in the data-bound MyFreezableCollection. Even though nothing is added to or removed from the list, the dependency property change callback is invoked.

The change notification handler will only notify you when the value of the property changes, so in this case if the freezable collection changes to a new collection. In your property changed handler you need to subscribe to the CollectionChanged event and in that event you need to subscribe to the PropertyChanged event on the new item. Now, finally, you have an event that will allow you to react to changes in properties of items belonging to a freezable collection that is a dependency property. Remember to unsubscribe to the old collection's and old item's events.

The thing is that you subscribe only for the FieldHeadings property changes, meaning you will receive notifications only if someone assigns a new instance of the collection itself, using, for example, the FeildHedings property setter.
In order to receive notifications when the Layout property changes, you have to subscribe to it on each individual instance of FieldHeading.

Related

nested property propertychanged event only fired once in usercontrol in silverlight

There are two Viewmodels, both of them had implemented the INotifyPropertyChanged interface (I have called the OnpropertyChanged("propertyname") in my actual code).
Public Class A{
public B BProperty
{
get
{
return _BProperty;
}
set
{
if (_BProperty != null)
_BProperty.PropertyChanged -= _BProperty_PropertyChanged;
_BProperty = value;
OnPropertyChanged("BProperty");
if (_BProperty != null)
_BProperty.PropertyChanged += _BProperty_PropertyChanged;
}
}
void _BProperty_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "status")
{
OnPropertyChanged("BProperty");
}
}
B _BProperty;
}
Public Class B
{
public int status{get;set;}
}
I also had a userControl:
<MyUserControl ...
... >
<Grid>
</Grid>
</MyUserControl>
And I had a dependencyProperty:
/// <summary>
/// File status
/// </summary>
public int Filestatus
{
get { return (int)GetValue(FilestatusProperty); }
set { SetValue(FilestatusProperty, value); }
}
public static readonly DependencyProperty FilestatusProperty =
DependencyProperty.Register(
"Filestatus",
typeof(int),
typeof(MyUserControl),
new PropertyMetadata(0, OnFilestatusPropertyChanged));
private static void OnFilestatusPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyUserControl control = (MyUserControl)d;
if (e.NewValue != null)
{
}
}
edit:2015/09/21
Add the get/set methods:
public static readonly DependencyProperty FileStatusProperty = DependencyProperty.RegisterAttached(
"FileStatus", typeof(int), typeof(FileStatusIconControl), new PropertyMetadata(0, PropertyChangedCallback));
public static int GetFileStatus(DependencyObject source)
{
return (int)source.GetValue(FileStatusProperty);
}
public static void SetFileStatus(DependencyObject target, int value)
{
target.SetValue(FileStatusProperty, value);
}
private static void PropertyChangedCallback(
DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
FileStatusIconControl fsic = dependencyObject as FileStatusIconControl;
if(fsic != null)
wahahahahaha;
}
edit end
I used this userControl in my mainPage like this:
<mainPage ...
...>
<Grid>
<MyUserControl Filestatus={Binding Bproperty.status} />
<TextBox Text={Binding Bproperty.status} />
</Grid>
</mainPage>
The datacontext of mainPage is an instance of Class A.
My question is:
When status is changed, the text of textbox is changed, but the OnFilestatusPropertyChanged method only was called once when Usercontrol is loaded. Why?
Thanks.
I will start by saying that while I was looking into your question I ran into some problems with the code you have provided. I appreciate that somewhere you have some real code which has a problem and you cannot share this code with us so have tried to reproduce the problem in a smaller set of files.
However, if you do do this, please at least verify that the code you have provided runs and exhibits the problem. It is evident that you haven't tried to run your sample code (particularly the XAML) as there are problems with it:
attribute values should be surrounded in double-quotes,
the binding path Bproperty.status should be BProperty.status (first P capitalised).
All these things slow down someone trying to help you. Worse still, when I do find a problem with your code I can't be sure whether it's a genuine problem that your real code also has or whether it's something you introduced putting together your sample code. So all I can do is point out all the problems I find in the hope that one of them is the problem you have in your real code.
Firstly, your TextBox's Text property binding doesn't contain Mode=TwoWay. In WPF, this binding is TwoWay by default, but in Silverlight all bindings are OneWay by default. If you are familiar with WPF, this may be why you omitted Mode=TwoWay.
Secondly, I don't see why you have implemented class B as you have, apparently leaving it up to class A to fire property-changed events on its behalf. This approach doesn't work: when Silverlight updates the value in the status property of a B instance, it does so by calling the status setter of the B instance. Your B class has only an autogenerated property setter, which certainly doesn't fire the PropertyChanged event. Because this event wasn't fired, Silverlight doesn't know that is has some updates to do, and furthermore your A class isn't aware that it has changed either.
I would implement INotifyPropertyChanged in the usual way in class B, by calling OnPropertyChanged in the status setter. I would also remove the BProperty_PropertyChanged event handler in class A as I don't think it does you any favours.

Binding to a static property is not responding to PropertyChanged

I am fairly new to XAML and WPF and have read numerous examples of how bind control properties but none seem to apply to my problem.
I have a static class Analyse that inherits INotifyPropertyChanged
Summary of the code below
class Analyse : INotifyPropertyChanged
{
public static DataSet moodleData; // Dataset containing the log data for analysis
private static bool dataPresent = true;
public static Boolean DataPresent
{
get { return dataPresent; }
set
{
if (dataPresent != value)
{
dataPresent = value;
NotifyStaticPropertyChanged("DataPresent");
}
}
}
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged
= delegate { };
private static void NotifyStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
#endregion
public static void clearData()
{
try
{
moodleData.Clear();
DataPresent = false;
}
catch { }
}
}
My XAML includes the name space local
<Window x:Name="TheMainWindow" x:Class="MoodleLogAnalyse.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}"
xmlns:local="clr-namespace:MoodleLogAnalyse"
Title="MainWindow" Height="556.88" Width="793" WindowStartupLocation="CenterScreen">
And the button is bound correctly
<Button Name="OpenButton" Command="Open"
IsEnabled="{Binding Source={x:Static local:Analyse.DataPresent},
Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Content="Open Grades" />
The property is definitely bound to the IsEnabled, changing the dataPresent definition manually in code enables and disables the button but a dynamic change such as switching between true and false such as calling the clear data method does not change the IsEnabled state of the button at run time.
The property change event is triggering as I have inserted break point to check.
I cannot see where I am going wrong with this! Any help would be appreciated.
The Source property is used to store the object which will be used as the alternative binding source (by default, bindings use DataContext).
<Button IsEnabled="{Binding Source={x:Static local:Analyze.DataPresent}}" />
So when you use the code above then the Static extension provides the current value of the DataPresent property and that value is stored in the Source property. In this case the binding uses a boxed constant value (true or false) as the source.
<Button IsEnabled="{Binding Path=(local:Analyze.DataPresent)}" />
But when you specify the Path property, the binding locates your static class and binds to the property. In this case the static DataPresent property is the source and the StaticPropertyChanged event is used to notify the binding about updates.
The INotifyPropertyChanged cannot be used in the case of binding to a static property because there is no instance is used to access that property. The corresponding static event must be used instead.
The name of the event must be equal to StaticPropertyChanged and its type must be EventHandler<PropertyChangedEventArgs>. Or the name must consist of two parts: the property name and suffix Changed. In the last case the event's type must be EventHandler because the event notifies only about changes of the corresponding property.
This means that:
The class Analyze can be static because INotifyPropertyChanged isn't necessary.
The event StaticPropertyChanged can be renamed to DataPresentChanged and its type can be changed to EventHandler.
In other words:
public static class Analyze
{
public static DataSet moodleData; // Dataset containing the log data for analysis
private static bool dataPresent = true;
public static Boolean DataPresent
{
get { return dataPresent; }
set
{
if (dataPresent != value)
{
dataPresent = value;
DataPresentChanged(null, EventArgs.Empty);
}
}
}
public static event EventHandler DataPresentChanged = delegate { };
}
Useful links:
Binding.Source Property
Binding to static properties

"Bindable Method" with OneWayToSource mode

I need to write a custom control that looks like a TextBox and that contains a method called Refresh() which main purpose will be to clear the Text and to roll back few other values.
The method shall become bindable somehow so that others can bind a property from their ViewModel with it. Hence why I am thinking that inside my custom control I will need an dependency property of type Action.
So far so logical but next problem is the method/dp may no get overriden on control side once users sets a two way binding on it. Basically I always have to deliver the method wrapper as Action to ViewModel and inside ViewModel other users may call it.
How do I do all this? It seems to me that I have to somehow get the binding of the method work like OneWayToSource.
I apologize in case its a duplicate. Futhermore thanks in advance guys.
EDIT: Please no alternative solutions. Those are the requirements and I have to stick to them.
I think that the simplest thing you can do here is to expose a bool property, maybe called IsCleared, and just call your method from that property when it becomes true. Exposing ICommand and/or delegate objects transfers the functionality out of your control, so you can't use those.
#ninjahedgehog, why can't you use a bool 'switch' property? Your requirement says 'so that others can bind a property from their ViewModel with it'... they can bind to a bool property from their view model. In my opinion, it seems to be your only option. As I said earlier, you can't use ICommand and/or delegate objects as that would transfer the functionality out of your control - that would enable other developers to write their own functionality rather than to just call yours.
What you really want is a method on your control that they could call from their view model... but view models shouldn't have any knowledge about the view controls, so you can't do that. The next best thing to that is creating a method that is called when a property is given a certain value. Here you have a few choices.
If you really don't like the bool switch idea, then how about an enum property? Create an enum with specific values like ClearText and whatever other functionality you would like to expose. Then the other developers simply set this property to the relevant instance to instantiate that functionality... I only suggested the bool switch property because it seems as if you only want to expose one piece of functionality.
One last point to note about using the bool switch property... as it is a switch, you need to reset it after use, or just never actually set it:
public bool IsTextClear
{
get { if (value) ClearText(); }
}
I dont know why you need this coz the person who is using your control can directly call the method from the code behind. But if you want that there should be some property like ClearMe on control and when set to true it should clear the control then you can define the dependency property and listen to its change in control like below and call Refresh from there.
public static readonly DependencyProperty ClearMeProperty = DependencyProperty.Register
(
"ClearMe",
typeof(bool),
typeof(MyControl),
new FrameworkPropertyMetadata(false, OnClearMeChanged)
);
public bool ClearMe
{
get { return (bool)GetValue(ClearMeProperty); }
set { SetValue(ClearMeProperty, value); }
}
private static void OnClearMeChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as MyControl;
if((bool)e.NewValue)
{
control.Refresh()
}
}
and you can bind this property to your ViewModel property. whenever ViewModel property will change to true. Property Change will be fired in control and will refersh it.
I editted my answer, as I wasn't understanding what you wanted. The only way I could come up with to do what you want was to use an Action DependencyProperty on the CustomControl and bind that to the ViewModel using a OneWayToSource binding, that way the Action from the control gets sent to the viewmodel. Within your customcontrol, you can test to make sure that only OneWayToSource binding is used and do something if not.. in this case, I add some text and made the background red.
View
<UserControl x:Class="WpfApplication1.Views.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:controls="clr-namespace:WpfApplication1.Controls">
<UserControl.Resources>
<vm:TestViewModel x:Key="TestViewModel" />
</UserControl.Resources>
<StackPanel DataContext="{StaticResource TestViewModel}">
<StackPanel Orientation="Horizontal" Height="30">
<controls:CustomTextBox Width="300" Refresh="{Binding RefreshAction, Mode=OneWayToSource}" />
<Button Content="Refresh" Width="80" Command="{Binding RefreshFromView}" />
</StackPanel>
</StackPanel>
ViewModel
using System;
using System.ComponentModel;
namespace WpfApplication1.ViewModels
{
public class TestViewModel : INotifyPropertyChanged
{
public TestViewModel()
{
RefreshFromView = new RelayCommand(ExecuteRefreshFromView);
}
public Action RefreshAction { get; set; }
public RelayCommand RefreshFromView { get; set; }
private void ExecuteRefreshFromView(object parameter)
{
if (RefreshAction != null)
RefreshAction();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyOfPropertyChange(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Custom Control
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace WpfApplication1.Controls
{
public class CustomTextBox : TextBox
{
public CustomTextBox()
{
this.Loaded += CustomTextBox_Loaded;
}
void CustomTextBox_Loaded(object sender, RoutedEventArgs e)
{
BindingExpression bindingExpression = GetBindingExpression(RefreshProperty);
BindingMode mode = bindingExpression.ParentBinding.Mode;
if (mode != BindingMode.OneWayToSource)
{
Text = "Use OneWayToSource Binding only!";
Background = new SolidColorBrush(Colors.Red);
}
Refresh = new Action(DoRefresh);
}
private void DoRefresh()
{
Text = null;
}
public Action Refresh
{
get { return (Action)GetValue(RefreshProperty); }
set { SetValue(RefreshProperty, value); }
}
public static readonly DependencyProperty RefreshProperty = DependencyProperty.Register("Refresh", typeof(Action), typeof(CustomTextBox));
}
}
You could use a Command:
public class Command : ICommand
{
public void Execute(object parameter)
{
// Do whatever you have to do
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
In your ViewModel:
public ICommand Command { get; set; }
In your XAML (assuming that your Custom Control is composed of a TextBox and a Button for example):
<Button Click="{Binding Command}" />

Dependency Property in WPF

I am working on a DependencyProperty for my Avalon dock controller. Here is some sample code which i have currently working on.
Requirement is: Create all dependency properties in one single class and access the property in View. Something like this.
<Button isPaneVisible="true"> or <Button isPaneVisible="{Staticresource path=name, Mode=twoway">
Could you please help me to reslove this issue?
namespace Avatar.UI.ViewModel
{
internal class DependencyPropertyClass : DependencyObject
{
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.RegisterAttached("IsPaneVisible", typeof(bool), typeof(DependencyPropertyClass),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsPaneVisible_PropertyChanged));
private static void IsPaneVisible_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
/// <summary>
/// Sets the IsPaneVisible for an element.
/// </summary>
public bool IsPaneVisible
{
get { return (bool)GetValue(IsPaneVisibleProperty); }
set
{
SetValue(IsPaneVisibleProperty, value);
}
}
}
}
<UserControl x:Class="Avatar.UI.View.ContentView"
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"
xmlns:avalonDock="http://avalondock.codeplex.com"
xmlns:local="clr-namespace:Avatar.UI.ViewModel"
d:DesignHeight="300" d:DesignWidth="300">
<Button IsPaneVisible="true"></Button
</UserControl>
Defining an attached dependency property also requires the definition of static get and set accessor methods. See Custom Attached Properties for more information. Note also that your class does not necessarily need to be derived from DependencyObject as long as it only defines attached properties. But it is always a good idea to define such properties in a public class.
public class DependencyPropertyClass
{
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.RegisterAttached("IsPaneVisible", typeof(bool), typeof(DependencyPropertyClass),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsPaneVisible_PropertyChanged));
private static void IsPaneVisible_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static bool GetIsPaneVisible(DependencyObject obj)
{
return (bool)obj.GetValue(IsPaneVisibleProperty);
}
public static void SetIsPaneVisible(DependencyObject obj, bool value)
{
obj.SetValue(IsPaneVisibleProperty, value);
}
}
And as Cyborgx37 has pointed out, you would use an attached property in XAML like this:
<Button local:DependencyPropertyClass.IsPaneVisible="True" />
I could be wrong, but I think you are looking for this:
<Button local:DependencyPropertyClass.IsPaneVisible="true"></Button>
You have to specify the namespace, since IsPaneVisible is not part of the "http://schemas.microsoft.com/winfx/2006/xaml/presentation" namespace.
See: Attached Properties Overview
EDIT
It's been a while since I've done this, so things are slowly coming back to me as I scan your code. For an attached property, you cannot use an instance property to get/set the property. You must create static Get<PropertyName> and Set<PropertyName> functions:
public static void SetIsPaneVisible(DependenyObject target, Boolean value)
{
target.SetValue(IsPaneVisibleProperty, value);
}
public static bool GetIsPaneVisible(DependenyObject target)
{
return (bool)target.GetValue(IsPaneVisibleProperty);
}
Seriously... please read the linked article. It's all explained there.
The dependency property should derive the base calss for which you are going to create dependency property. for example, if you are going to create dependency property for button, then derive the base class button to your class.
This is how I have resolved my issue.

How to modify inner control property by setting container control dependency property?

I am creating a control in WPF which have inside a label and I have created a dependency property in order to modify the label's visibility property.
My problem is that I cant find a way to change my label's visibility property at the same time my dependency property is assigned.
My code is as below:
public static readonly DependencyProperty captionVisibleProperty = DependencyProperty.Register(
"CaptionVisible",
typeof(bool),
typeof(MyCustomControl));
public bool CaptionVisible
{
get
{
return (bool)GetValue(captionVisibleProperty);
}
set
{
SetValue(captionVisibleProperty, value);
ShowCaption();
}
}
private void ShowCaption()
{
if (CaptionVisible)
{
captionLabel.Visibility = System.Windows.Visibility.Visible;
}
else
{
captionLabel.Visibility = System.Windows.Visibility.Collapsed;
}
}
As you can see I have tried to call my ShowCaption() method when my property is set, but nothing happens.
So, what I am supposed to do in order to get it done?
Hope someone can help me. Thank you in advance.
ShowCaption() shouldn't be necessary. Instead, just bind the label's Visibility property to your CaptionVisible property in xaml. It's also best to follow the Model-View-ViewModel design pattern for keeping your code organized. This means putting the logic for controlling your user interface (the View) in separate ViewModel classes, and then assigning ViewModel to that View's DataContext property.
That will make the binding a lot easier. Referencing properties that belong to user interface elements can sometimes be a bit of a hassle in WPF. By contract, WPF's bindings system was specifically designed to make it easy to get at the contents of a user interface element's DataContext.
You'll also need to use the handy-dandy BooleanToVisiblityConverter to make the binding work, since the Visibility property's type isn't bool. I like to put it in in the window's (or control's) resource dictionary for easy access:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Label Visibility="{Binding Path=CaptionVisible,
Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- label content -->
</Label>
As a side note, unless CaptionVisible is going to be the target of a binding, making it a dependency property is overkill. In this binding it's only the source, so just implementing INotifyPropertyChanged would be sufficient:
class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private bool _captionVisible;
public bool CaptionVisible
{
get { return _captionVisible; }
set
{
if(_captionVisible != value)
{
_captionVisible = value;
RaisePropertyChanged("CaptionVisible");
}
}
}
}
I have found a solution to this problem:
If you need to link a nested control dependency property to a container control dependency property you can do this:
public static readonly DependencyProperty captionLabelVisibilityProperty = DependencyProperty.Register(
"CaptionVisibility",
typeof(Visibility),
typeof(MyContainerControl),
new FrameworkPropertyMetadata(
VisibilityPropertyChangedCallback));
public Visibility CaptionVisibility
{
get
{ return (Visibility)GetValue(captionLabelVisibilityProperty); }
set
{ SetValue(captionLabelVisibilityProperty, value); }
}
private static void VisibilityPropertyChangedCallback(DependencyObject controlInstance, DependencyPropertyChangedEventArgs args)
{
MyContainerControl myContainerControlInstance = (MyContainerControl)controlInstance;
myContainerControlInstance.myLabel.Visibility = (Visibility)args.NewValue;
}

Categories

Resources