Binding visibility to static property
I have a control that has a label on it, that I would like to hide or show based on a global menu item for all instances of my control. If I click the button to hide labels, I want to to hide all of them.
My xaml looks like this:
<TextBlock Name="_label" Visibility="{Binding LabelShown}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
in my code behind I have a property:
private static Visibility _labelShown;
public static Visibility LabelShown
{
get { return _labelShown; }
set { _labelShown = value; }
}
And I set DataContext = this;
When I change t开发者_StackOverflow中文版he static property, nothing happens. I assume this is because no controls are getting a property changed notification. I cannot implement INotifyPropertyChanged on it, because I cannot reference the non static property changed handler from my static property.
I feel like maybe this isn't the best way to do this, but I would really like to have one button (many levels above my actual control) drive the visibility for all instances.
CodeNaked's solution works, but it uses a Singleton which has downsides when doing unit-testing. I prefer to approach global access problems by just having one settings instance at the application root, i.e. the App
-class.
e.g.
public partial class App : Application
{
private static Settings _settings = new Settings();
public static Settings Settings
{
get { return _settings; }
}
...
Where this property contains all the settings for the application. Binding then looks like this:
"{Binding Source={x:Static local:App.Settings}, Path=LabelsShown}"
Edit: If you are worried about dependencies you could also inject a reference to those settings in the constructor of any class where you need it, using its minimal interface.
e.g.
public class MyControl : ContentControl
{
public interface IMyControlSettings
{
public bool LabelsShown { get; set; }
}
private IMyControlSettings _settings;
public MyControl(IMyControlSettings settings)
{
_settings = settings;
DataContext = _settings; // For easy binding, in most cases you probably do not want to do that since it prevents DataContext inheritance.
}
}
public class Settings : Test.MyControl.IMyControlSettings, INotifyPropertyChanged
{
public bool LabelsShown { get; set; }
...
}
You can do something like this:
public class MySettings : INotifyPropertyChanged {
private MySettings() {
}
private Visibility _labelShown;
public Visibility LabelShown
{
get { return _labelShown; }
set {
_labelShown = value;
// Raise PropertyChanged event for LabelShown
}
}
private static MySettings _instance;
public static MySettings Instance
{
get {
if (_instance == null)
_instance = new MySettings();
return _instance;
}
}
}
Then bind to it like {Binding Path=LabelShown, Source={x:Static local:MySettings.Instance}}
The only thing you need to add is the local xmlns which would be like xmlns:local="clr-namespace:MyNamespace"
I found a kinda lame workaround:
public static Visibility LabelShown
{
get { return _labelShown; }
set
{
_labelShown = value;
if ( StaticEvent != null )
{
StaticEvent();
}
}
}
private static event Action StaticEvent;
public event PropertyChangedEventHandler PropertyChanged
{
add { StaticEvent += () => value(this, new PropertyChangedEventArgs("LabelShown")); }
remove { StaticEvent -= () => value(this, new PropertyChangedEventArgs("LabelShown")); }
}
It works, but I am a little worried about the remove handler actually being able to remove the anonymous method like that. Would that cause memory problems if many controls are disposed?
I tend to prefer CodeNaked's solution, but I wanted to offer this for discussion.
精彩评论