我正在構建一個 WPF/MVVM 應用程式,該應用程式在彼此下方顯示一些串列。除了串列之外,我的 MainViewModel 還包含一個文本框,我想將其文本內容用作串列的過濾器。但是,這些串列不在 MainViewModel 中,而是在子控制元件 (UserControl2_*) 中。
如果過濾器屬性與 ICollectionView 位于同一 ViewModel 中,則過濾有效(請參閱ViewModel2.cs 中的CollectionViewFilter ),但我不明白如何將過濾器應用于多個子視圖模型。
是否有符合 MVVM 的方法將過濾器傳遞給子控制元件?或者我是否需要向上傳遞集合,以便我可以從 ViewModel 訪問它們,其中還設定了 filter 屬性?
如果您希望我上傳或修改更多代碼,請告訴我,我將編輯我的問題。
___________________ ___________________
|Search: | |Search: FG |
|___________________| |___________________|
|Collection | |Collection |
| _________________ | | _________________ |
||UserControl1_1 || ||UserControl1_1 ||
|| _______________ || || _______________ ||
|||UserControl2_1 ||| |||UserControl2_1 |||
|||* ABCDEF ||| |||* BCDEFG |||
|||* BCDEFG ||| |||* CDEFGH |||
|||* CDEFGH ||| |||_______________|||
|||_______________||| ||_________________||
|| _______________ || | _________________ |
|||UserControl2_2 ||| ||UserControl1_2 ||
|||* ABCDEF ||| || _______________ ||
|||* UVWXYZ ||| => |||UserControl2_3 |||
|||_______________||| |||* BCDEFG |||
| _________________ | |||_______________|||
||UserControl1_2 || || _______________ ||
|| _______________ || |||UserControl2_4 |||
|||UserControl2_3 ||| |||* CDEFGH |||
|||* LMNOPQ ||| |||_______________|||
|||* BCDEFG ||| ||_________________||
|||* UVWXYZ ||| |___________________|
|||_______________|||
|| _______________ ||
|||UserControl2_4 |||
|||* ABCDEF |||
|||* CDEFGH |||
|||_______________|||
||_________________||
|___________________|
主視窗.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new CollectionViewModel();
}
}
MainViewModel.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding ViewModels1}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:UserControl1 />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
集合視圖模型.cs
public class CollectionViewModel : ObservableObject
{
public ObservableCollection<ViewModel1> ViewModels1 { get; set; }
// ...
}
ViewModel1.cs
public class ViewModel1 : ObservableObject
{
public ObservableCollection<ViewModel2> ViewModels2 { get; set; }
// ...
}
ViewModel2.cs
public class ViewModel2 : ObservableObject
{
private readonly ObservableCollection<ViewModel3> ViewModels3 { get; set; }
public ICollectionView CollectionView3 { get; }
private string _filter = string.Empty;
public string Filter
{
get => _filter;
set => SetProperty(ref _filter, value);
}
public ViewModel2(Model2 model2)
{
model = model2;
ViewModels3 = new ObservableCollection<ViewModel3>();
CollectionView3 = CollectionViewSource.GetDefaultView(ViewModels3);
CollectionView3.Filter = CollectionViewFilter;
}
private bool CollectionViewFilter(object obj)
{
if (obj is ViewModel3 viewModel)
{
return viewModel.Name.Contains(Filter, StringComparison.InvariantCultureIgnoreCase);
}
return true;
}
// ...
}
ViewModel3.cs
public class ViewModel3 : ObservableObject
{
private Model3 _model;
public ViewModel3(Model3 model3)
{
_model = model3;
}
public string Name
{
get => _model.Name;
set => SetProperty(_model.Name, value, _model, (model, name) => model.Name = name);
}
}
基于mm8的答案的解決方案:
我擴展了我的 CollectionViewModel.cs 如下:
private string filterText = string.Empty;
public string FilterText
{
get => filterText;
set
{
SetProperty(ref filterText, value);
foreach(var vm1 in ViewModels1)
{
foreach(var vm2 in vm1.ViewModels2)
{
vm2.Filter = value;
}
}
}
}
在 ViewModel2 中缺少 CollectionView 的重繪 :
public string Filter
{
get => _filter;
set
{
SetProperty(ref _filter, value);
CollectionView3.Refresh();
}
}
基于mm8 的第二條建議的解決方案(使用信使):
由于我使用的是 Microsoft.Toolkit.MVVM,因此我使用了IMessenger界面。為此,我添加了類FilterTextChangedMessage.cs:
public class FilterTextChangedMessage : ValueChangedMessage<string>
{
public FilterTextChangedMessage(string value) : base(value)
{
}
}
我將CollectionViewModel.cs的 FilterText 屬性更改如下:
public string FilterText
{
get => filterText;
set
{
SetProperty(ref filterText, value);
WeakReferenceMessenger.Default.Send(new FilterTextChangedMessage(value));
}
}
我將ViewModel2.cs更改如下:
public class ViewModel2 : ObservableRecipient, IRecipient<FilterTextChangedMessage> {
public ViewModel2(Model2 model)
{
WeakReferenceMessenger.Default.Register<FilterTextChangedMessage>(this, (r, m) => {
Filter = m.Value as string;
});
}
}
uj5u.com熱心網友回復:
由于定義CollectionViewModel了該FilterText屬性的 ,已經通過該ViewModels1屬性對子視圖模型具有強參考,因此您可以通過遍歷此源集合來設定這些過濾器。
就 MVVM 而言,這非常好。應用程式邏輯保留在它所屬的視圖模型中。
然而,從一種視圖模型型別到另一種視圖模型型別保持強參考確實會引入一種耦合,這往往會使應用程式更難維護。但這是該ViewModels1物業已經出現的另一個問題。
此類問題通常通過使用信使或事件聚合器在不同視圖模型之間進行通信來解決。在這種情況下,您將發送某種過濾器事件,CollectionViewModel并在子視圖模型中處理此事件。
uj5u.com熱心網友回復:
您必須CollectionView在某些時候鏈接s 和 filter 方法,因此您的 UserControl 需要允許這樣做。我可以想到三種方法來做到這一點:
讓您的 UserControl 處理它自己的
CollectionView,并添加一個方法來配置您的過濾器(將一個Predicate<object>作為引數并將其設定為 Filter 屬性)。讓您的 UserControl 公開一個
CollectionView為您的串列回傳 a 的方法,并在 MainWindow 中執行所有操作。不太漂亮,但仍然可能沒問題,并且當您只想顯示沒有任何選項的串列時避免創建不必要的東西。最“MVVM”的方法是將 a 添加
DependencyProperty到您的 UserControl 中,并PropertyChangedCallback更新物件的過濾。然后,您可以系結到主視窗視圖模型的屬性,并根據您的文本欄位更新該屬性。這是我擁有的一些代碼的示例:
public static readonly DependencyProperty BlurRadiusProperty =
DependencyProperty.Register("BlurRadius", typeof(double), typeof(FastShadow),
new FrameworkPropertyMetadata(
0.0,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback((o, e) =>
{
var f = (FastShadow)o;
if ((double)e.NewValue < 0)
f.BlurRadius = 0;
f.CalculateGradientStops();
})));
public double BlurRadius
{
get => (double)this.GetValue(BlurRadiusProperty);
set => this.SetValue(BlurRadiusProperty, value);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/408093.html
標籤:
上一篇:擊鍵事件不在C#WPF中執行
