WPF使用 INotifyPropertyChanged 實現資料驅動
如下圖,有這麼一個常見需求,在修改表單明細的蘋果價格時,總價會改變,同時單據總和也隨之改變。
按照Winfrom事件驅動的思想來做的話,我們就需要在將UI的修改函式繫結到CellEdit事件中來實現。
但是對於WPF,我們完全可以利用WPF的 INotifyPropertyChanged 介面來實現。
首先我們通過nuget引入WPF常用的自動首先通知的第三方包 PropertyChanged.Fody ,它的作用是凡是實現了 INotifyPropertyChanged 的類的屬性預設都會通知前端
然後建立訂單和訂單明細兩個基本類,並實現 INotifyPropertyChanged
public class DJ : INotifyPropertyChanged
{
public int ID { get; set; }
public double SumPrice
{
get
{
return MXs.Sum(it => it.Price);
}
}
public ObservableCollection<Models.DJMX> MXs { get ; set; } = new ObservableCollection<DJMX>();
public event PropertyChangedEventHandler PropertyChanged;
}
public class DJMX : INotifyPropertyChanged
{
public object DJ { get; set; }
public object MainWindowViewModel { get; set; }
public string Name { get ; set; }
private double price;
public double Price
{
get { return price; }
set
{
price = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
前端程式碼
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" ItemsSource="{Binding DJs}">
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="訂單號" Binding="{Binding ID}"/>
<DataGridTextColumn Width="*" Header="總價" Binding="{Binding SumPrice}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" SelectionUnit="CellOrRowHeader"
ItemsSource="{Binding MXs}">
<DataGrid.Columns>
<DataGridTextColumn Header="商品名" Width="100" Binding="{Binding Name}"/>
<DataGridTextColumn Header="價格" Width="100" Binding="{Binding Price, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
<StackPanel Grid.Row="1" VerticalAlignment="Center" Orientation="Horizontal">
<TextBlock Text="單據總和: "/>
<TextBlock Text="{Binding AllSumPrice}"/>
</StackPanel>
</Grid>
前端對應的ViewModel
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
DJs = new ObservableCollection<Models.DJ>()
{
new Models.DJ(){ ID=1},
new Models.DJ(){ ID=2},
new Models.DJ(){ ID=3},
new Models.DJ(){ ID=4},
new Models.DJ(){ ID=5}
};
foreach (var dj in DJs)
{
dj.MXs = new ObservableCollection<Models.DJMX>()
{
new Models.DJMX() { Name="蘋果", Price=100 },
new Models.DJMX() { Name="鴨梨", Price=200 },
new Models.DJMX() { Name="香蕉", Price=300 },
};
}
}
public double AllSumPrice
{
get
{
return DJs.Sum(it => it.SumPrice);
}
}
public ObservableCollection<Models.DJ> DJs { get; set; } = new ObservableCollection<Models.DJ>();
public event PropertyChangedEventHandler PropertyChanged;
}
執行除錯一下
發現價格修改並沒有影響到總價和總和, 結果並不如預期的那樣,我們分析一下:
來看總價和總和屬性的定義,兩個都是隻讀的,因為沒有Set的屬性,所以Fody是無法進行通知的,準確的說,是 PropertyChanged 沒有設定到該屬性。
例如,價格的屬性程式碼完整其實是這樣的
在價格屬性改變後,會通過綁定價格屬性的前端進行修改。
所以,如果我們想讓價格修改的同時,總價和總和也要通知到,即可以在價格屬性的Set方法中,增加通過SumPrice和AllSumPrice的程式碼。
而 PropertyChanged 需要傳入一個當前屬性所在的示例和當前屬性的名稱,在這裡,我通過修改 OnPropertyChanged 增加一個 OnNavigationObjDJPropertyChanged 方法,
另外訂單明細也需要定義兩個新的obj屬性用來存放需要通知的例項,達到類似EF導航屬性的效果,最終的 DJMX 類程式碼如下
public class DJMX : INotifyPropertyChanged
{
public object DJ { get; set; }
public object MainWindowViewModel { get; set; }
public string Name { get; set; }
private double price;
public double Price
{
get { return price; }
set
{
price = value;
OnPropertyChanged(new PropertyChangedEventArgs("price"));
OnNavigationObjDJPropertyChanged(DJ, new PropertyChangedEventArgs("SumPrice")); //new
OnNavigationObjDJPropertyChanged(MainWindowViewModel, new PropertyChangedEventArgs("AllSumPrice")); //new
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
//new
public void OnNavigationObjDJPropertyChanged(object objTargert,PropertyChangedEventArgs e)
{
if (PropertyChanged != null&& objTargert!= null)
{
PropertyChanged(objTargert, e);
}
}
}
同時ViewModel 的程式碼也需要在資料例項化時,增加傳入兩個通知的例項,程式碼如下:
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
DJs = new ObservableCollection<Models.DJ>()
{
new Models.DJ(){ ID=1},
new Models.DJ(){ ID=2},
new Models.DJ(){ ID=3},
new Models.DJ(){ ID=4},
new Models.DJ(){ ID=5}
};
foreach (var dj in DJs)
{
dj.MXs = new ObservableCollection<Models.DJMX>()
{
new Models.DJMX() { DJ=dj, MainWindowViewModel=this, Name="蘋果", Price=100 }, //changed
new Models.DJMX() { DJ=dj, MainWindowViewModel=this, Name="鴨梨", Price=200 }, //changed
new Models.DJMX() { DJ=dj, MainWindowViewModel=this, Name="香蕉", Price=300 }, //changed
};
}
}
public double AllSumPrice
{
get
{
return DJs.Sum(it => it.SumPrice);
}
}
public ObservableCollection<Models.DJ> DJs { get; set; } = new ObservableCollection<Models.DJ>();
public event PropertyChangedEventHandler PropertyChanged;
}
我們再除錯執行一次
完美!!!
翻譯 朗讀 複製 正在查詢,請稍候…… 重試 朗讀 複製 複製 朗讀 複製 via 百度翻譯 譯