簡體   English   中英

在沒有INotifyPropertyChanged的情況下更新ObservableCollection成員屬性的綁定

[英]Updating binding for ObservableCollection member property without INotifyPropertyChanged

我有一個WPF View綁定到實現INotifyPropertyChangedViewModel上。 在該View ,我有一個DataGrid ,其項目綁定到ViewModelObservableCollection ObservableCollection成員本身並不實現INotifyPropertyChanged DataGrid我有三列-兩個可編輯的DatePicker列和一個只讀的Checkbox列,當用戶選擇包含當前日期的日期范圍時應選中該列。

綁定在第一次加載視圖時起作用,但是當我更改其他兩列中的日期時,不會更新Checkbox的綁定,這大概是因為MyDomainObject類未實現INotifyPropertyChanged 我真的不想在這里實現該接口,因為ObservableCollection是從Web服務返回的類型,這樣做會迫使我創建和維護該類型的副本,而且我有很多類似的情況應用。 有什么方法可以強制Checkbox更新? 如果可能,我非常希望避免在后面使用代碼-如果必須打破該規則,我知道該怎么做。

以下代碼應大致說明問題:

XAML:

<DataGrid ItemsSource="{Binding MyDomainObjectCollection}">
    <DataGrid.Columns>

        <DataGridTemplateColumn Header="Start Date">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding StartDate, StringFormat=d}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <DatePicker SelectedDate="{Binding StartDate, UpdateSourceTrigger=PropertyChanged}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="End Date">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding EndDate, StringFormat=d}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <DatePicker SelectedDate="{Binding EndDate, UpdateSourceTrigger=PropertyChanged}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="Is Active">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding IsActive, Mode=OneWay}" IsEnabled="False" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>
</DataGrid>

查看模型:

public class MyViewModel : INotifyPropertyChanged {

    ObservableCollection<MyDomainObject> _myDomainObjectCollection;

    public ObservableCollection<MyDomainObject> MyDomainObjectCollection {
        get { return this._myDomainObjectCollection; }
        set { this._myDomainObjectCollection = value; this.OnPropertyChanged(); }
    }

    [...]

}

域對象:

public class MyDomainObject {

    public DateTime StartDate { get; set; }

    public DateTime EndDate { get; set; }

    public bool IsActive {
        get { return StartDate < DateTime.Now && EndDate > DateTime.Now; }
    }

}

如果您不想在域對象上實現INotifyPropertyChanged接口,則可以使用與ModelView具有相同接口並實現INotifyPropertyChanged的ViewModel來包裝DomainObject。

public class MyDomainObjectViewModel : INotifyPropertyChanged {

  private MyDomainObject _domainObject;

  public MyDomainObjectViewModel(MyDomainObject domainObject){
    _domainObject = domainObject;
  }

  public DateTime StartDate { 
    get{
      return _domainObject.StartDate;
    }
    set{
      _domainObject.StartDate = value;
      RaisePropertyChanged("StartDate");
      RaisePropertyChanged("IsActive");
    }
  }

  public DateTime EndDate { 
    get{
      return _domainObject.EndDate ;
    }
    set{
      _domainObject.EndDate = value;
      RaisePropertyChanged("EndDate");
      RaisePropertyChanged("IsActive");
    }
  }

  public bool IsActive {
    get { return StartDate < DateTime.Now && EndDate > DateTime.Now; }
  }

}

這樣,您的ObservableCollection的類型將為ObservableCollection<MyDomainObjectViewModel>

幾年前,我面臨着同樣的問題。 我發現的解決方案使用動態對象作為實現INotifyPropertyChanged的代理對象。 它通過反射掛接到getter和setter上,並在代理更改時更新源對象。

public class DynamicProxy : DynamicObject, INotifyPropertyChanged
{
    private readonly PropertyDescriptorCollection mPropertyDescriptors;


    protected PropertyInfo GetPropertyInfo(string propertyName)
    {
        return
            ProxiedObject.GetType()
                .GetProperties()
                .FirstOrDefault(propertyInfo => propertyInfo.Name == propertyName);
    }

    protected virtual void SetMember(string propertyName, object value)
    {
        var propertyInfo = GetPropertyInfo(propertyName);

        if (propertyInfo.PropertyType == value.GetType())
        {
            propertyInfo.SetValue(ProxiedObject, value, null);
        }
        else
        {
            var underlyingType = Nullable.GetUnderlyingType(propertyInfo.PropertyType);

            if (underlyingType != null)
            {
                var propertyDescriptor = mPropertyDescriptors.Find(propertyName, false);

                var converter = propertyDescriptor.Converter;
                if (converter != null && converter.CanConvertFrom(typeof (string)))
                {
                    var convertedValue = converter.ConvertFrom(value);
                    propertyInfo.SetValue(ProxiedObject, convertedValue, null);
                }
            }
        }
        RaisePropertyChanged(propertyName);
    }

    protected virtual object GetMember(string propertyName)
    {
        return GetPropertyInfo(propertyName).GetValue(ProxiedObject, null);
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        OnPropertyChanged(propertyName);
    }

    #region constructor

    public DynamicProxy()
    {
    }

    public DynamicProxy(object proxiedObject)
    {
        ProxiedObject = proxiedObject;
        mPropertyDescriptors = TypeDescriptor.GetProperties(ProxiedObject.GetType());
    }

    #endregion

    public override bool TryConvert(ConvertBinder binder, out object result)
    {
        if (binder.Type == typeof (INotifyPropertyChanged))
        {
            result = this;
            return true;
        }

        if (ProxiedObject != null && binder.Type.IsInstanceOfType(ProxiedObject))
        {
            result = ProxiedObject;
            return true;
        }
        return base.TryConvert(binder, out result);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = GetMember(binder.Name);
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        SetMember(binder.Name, value);
        return true;
    }

    #region public properties

    public object ProxiedObject { get; set; }

    #endregion

    #region INotifyPropertyChanged Member

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

假設我們有一個這樣的域對象:

public class DomainObject
{
    public string Name { get; set; }
}

和這個觀點:

<Window x:Class="WpfApplication1.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>
    <TextBox 
        VerticalAlignment="Top"
        Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}">            
    </TextBox>
</Grid>
</Window>

並為其分配代理,如下所示:

DataContext = new DynamicProxy(new DomainObject());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM