WPF中的单实例对象数据绑定

WPF的数据绑定非常强大,可以省去我们在winform下的不少难写代码。本文主要探讨一下WPF中单实例对象数据绑定。

WPF的单实例对象数据绑定的需求主要起源于我写的一个下载工具,我写了一个自动关机的功能,然后想把这个自动关机的状态同时双向绑定到工具栏和菜单中,而工具栏和菜单是分别在两个不同的UserControl中写的,它们之间不共享数据。这样把配置数据绑定到多个不同的控件的需求还有不少。

首先我想到的方式是将配置数据写成静态属性,然后通x:Static过标记直接绑定静态属性到控件上,但是很快就发现了这样的局限性:不能实现双向数据绑定。原因很简单:双向数据绑定的时候,是基于实现了INotifyPropertyChanged的对象通知的。静态属性的变更自然无法通知出去。

既然静态属性的形式不行,就想到了将数据绑定到单实例对象中,便用SINGLETON模式实现了一个,实例代码如下:

<StackPanel>
<CheckBox Content="AutoPowerOff1" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={x:Static src:ConfigData.Instance}}"/>
<CheckBox Content="AutoPowerOff2" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={x:Static src:ConfigData.Instance}}"/>
</StackPanel>


//SINGLETON模式
class ConfigData : System.ComponentModel.INotifyPropertyChanged
{

    public static ConfigData Instance = new ConfigData();

    private ConfigData() { }

    bool autoPowerOff = false;
    public bool AutoPowerOff
    {
        get { return autoPowerOff; }
        set { autoPowerOff = value; NotifyPropertyChange("AutoPowerOff"); }
    }

    #region INotifyPropertyChanged
成员

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    void NotifyPropertyChange(string proper)
    {
        if (PropertyChanged == null)
            return;
        PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(proper));
    }

    #endregion
}

这个代码毕竟简单,将两个checkbox的check状态分别绑定到ConfigData的AutoPowerOff属性中,通过静态属性Source={x:Static src:ConfigData.Instance}指定到同一个数据源,所以一个checkbox的状态变更会同步更新到另一个checkbox。实现了我们的需求。

SINGLETON模式非常简单有效,然而有一点让我们不大爽的地方,那就是不支持blend自动绑定,需要手工敲不少代码。Blend的数据绑定中,只能自动生成对象,然后将数据绑定到生产的对象中,类似这种形式。

<StackPanel>
    <StackPanel.Resources>
    
    <src:ConfigData x:Key="ConfigDataDataSource" d:IsDataSource="True"/>
    
    <src:ConfigData x:Key="ConfigDataDataSource1" d:IsDataSource="True"/>
    
    
    </StackPanel.Resources>
    
    <CheckBox Content="AutoPowerOff1" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={StaticResource ConfigDataDataSource}}"/>
    <CheckBox Content="AutoPowerOff2" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={StaticResource ConfigDataDataSource1}}"/>
</StackPanel

要让我们的单实例对象能在blend生成的绑定代码下工作,可以通过使用MonoState模式来实现。代码如下:

class ConfigData : System.ComponentModel.INotifyPropertyChanged
{
    static bool autoPowerOff = false;
    public bool AutoPowerOff
    {
        //
把这个属性设置为static也可以双向绑定,但由于blend不会列出对象的静态属性,需要手工输入属性名
        get { return autoPowerOff; }
        set { autoPowerOff = value; NotifyPropertyChange("AutoPowerOff"); }
    }

    #region INotifyPropertyChanged
成员

    static List<ConfigData> instanceList = new List<ConfigData>();
    System.ComponentModel.PropertyChangedEventHandler propertyChanged;
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged
    {
        add { propertyChanged += value; instanceList.Add(this); }
        remove { propertyChanged -= value; instanceList.Remove(this); }
    }

    static void NotifyPropertyChange(string proper)
    {
        foreach (var obj in instanceList)
        {
            if (obj.propertyChanged == null)
                continue;
            obj.propertyChanged(obj, new System.ComponentModel.PropertyChangedEventArgs(proper));
        }
    }

    #endregion
}

MonoState模式比较巧妙的将blend创建的多个对象的数据共享了起来,对于blend来说,创建了多个对象,而用起来却和一个实例对象一样。更具有通用性。

在WPF的数据绑定中使用MonoState模式关键是PropertyChangedEventHandler事件的通知,由于WPF是基于对象通知的,而所有的对象是共享的数据,因此需要把该事件通知到所有实例。这里我通过采用保存实例列表来实现所有通知的,比较简单,没有考虑效率问题(这种对象一共也创建不了几个),但应该有更高效的方式来实现同样的功能。

本文的代码中还存在一个问题,那就是在代码中对AutoPowerOff属性的访问问题,在不创建对象的前提下,一种方式是把它设置为static类型,这样就可以在代码中通过ConfigData.AutoPowerOff直接的访问,但这样blend不能直接列出该属性,需要手工输入一下。另外一种方式是综合SINGLETON模式创建一个静态的实例,在代码中通过ConfigData. Instance.AutoPowerOff访问。

原文地址:https://www.cnblogs.com/TianFang/p/1515562.html