我有一个主窗口,其中包含内容控件和两个单选按钮。 MainWindow.xaml:
<Grid>
<StackPanel >
<RadioButton Content="First"
IsChecked="True"
Command="{Binding FirstViewCommand}"/>
<RadioButton Content="Second"
Command="{Binding SecondViewCommand}"/>
</StackPanel>
<ContentControl Grid.Row="1"
Grid.Column="1"
Content="{Binding CurrentView}"/>
</Grid>
我绑定了我的用户控件到这个内容控件。这里我有组合框列表框和按钮。基于 Cobobox.SelectedElement 我需要在按下按钮后更改 ListBox 中的数据。 用户控制:
<Grid>
<ComboBox x:Name="BoxOfDetails"
ItemsSource="{Binding DetailsScheme}">
</ComboBox>
<Button Command="{Binding ProduceCommand}"
CommandParameter="{Binding ElementName=BoxOfDetails, Path=SelectedItem}" />
<ListBox x:Name="ListOfMaterials"
ItemsSource="{Binding Materials}" >
</ListBox>
</Grid>
我还有主视图模型,它是主窗口的数据上下文:
class MainVM : ObservableObject
{
public RelayCommand FirstViewCommand { get; set; }
public RelayCommand SecondViewCommand { get; set; }
public FirstVM FirstViewModel { get; set; }
public SecondVM SecondViewModel { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set => Set(ref _currentView, value);
}
public MainVM()
{
FirstViewModel = new FirstVM();
SecondViewModel = new SecondVM();
CurrentView = FirstViewModel;
FirstViewCommand = new RelayCommand(o =>
{
CurrentView = FirstViewModel;
});
SecondViewCommand = new RelayCommand(o =>
{
CurrentView = SecondViewModel;
});
}
和我的用户控件的视图模型:
class FirstVM : ObservableObject
{
private ObservableCollection<DetailScheme> _detailsScheme;
private ObservableCollection<Material> _materials;
public ObservableCollection<DetailScheme> DetailsScheme
{
get => _detailsScheme;
public ObservableCollection<Material> Materials
{
get => _materials;
set => Set(ref _materials, value);
}
public RelayCommand ProduceCommand { get; set; }
public FirstVM ()
{
_detailsScheme = GetDetailsScheme("D:/ DetailsScheme.json");
_materials = GetMaterials("D:/ Materials.json");
ProduceCommand = new RelayCommand(o =>
{***});
}
当我的用户控制切换时,我需要在列表框中保存信息(例如,用户在框中选择某些内容并按下按钮,列表框中的数据在切换控件后正在更新,我拥有与启动应用程序时相同的信息)
我添加了将信息保存到我的用户控件视图模型中的文件的方法:
public void SaveFirstVM()
{
SaveDetails(_details, "D:/ Details.json");
SaveMaterials(_materials, "D:/ Materials.json");
}
当我在命令中的主视图模型中切换视图时,我尝试调用它
SecondViewCommand = new RelayCommand(o =>
{
CurrentView = SecondViewModel;
FirstViewModel.SaveFirstVM()
});
但没有任何反应,我可以在每次按下用户控件视图模型中的生成按钮后调用此保存方法,但我认为这不是一个好方法。 我在我的项目中添加了一些断点,我只能建议问题是我的 UserControl 视图模型创建了两次,第一次在 MainVM 中,第二次在初始化发生时,当我更改数据时,它发生在初始化对象中,当我尝试从 mainVM 保存数据它保存其他未更改的对象
我使用一个名为
ObservableHandler<T>
的类,它可以让我观看另一个实现 INotifyPropertyChanged
的类并侦听 PropertyChanged
事件。
如果您要使用此功能,您可以监视其他 ViewModel 中的属性发生更改,而不必保存到文件中。
public class OtherViewModel
{
private ObservableHandler<ViewModel> _handler;
public OtherViewModel(ViewModel vm)
{
_handler = new ObservableHandler<ViewModel>(vm)
.Add(v => v.Name, OnOtherNameChanged);
}
private void OnOtherNameChanged(ViewModel vm)
{
// react to the other property changing
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
}
public class ObservableHandler<T> : IWeakEventListener
where T : class, INotifyPropertyChanged
{
private readonly WeakReference<T> _source;
private readonly Dictionary<string, Action> _handlers = new();
private readonly Dictionary<string, Action<T>> _handlersT = new();
public ObservableHandler(T source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
_source = new(source);
}
public ObservableHandler<T> Add(Expression<Func<T, object>> expression, Action handler)
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}
var source = GetSource();
if (source == null)
{
throw new InvalidOperationException("Source has been garbage collected.");
}
var propertyName = GetPropertyNameFromLambda(expression);
_handlers[propertyName] = handler;
PropertyChangedEventManager.AddListener(source, this, propertyName);
return this;
}
public ObservableHandler<T> Add(Expression<Func<T, object>> expression, Action<T> handler)
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}
var source = GetSource();
if (source == null)
{
throw new InvalidOperationException("Source has been garbage collected.");
}
var propertyName = GetPropertyNameFromLambda(expression);
_handlersT[propertyName] = handler;
PropertyChangedEventManager.AddListener(source, this, propertyName);
return this;
}
public ObservableHandler<T> AddAndInvoke(Expression<Func<T, object>> expression, Action handler)
{
Add(expression, handler);
handler();
return this;
}
public ObservableHandler<T> AddAndInvoke(Expression<Func<T, object>> expression, Action<T> handler)
{
Add(expression, handler);
handler(GetSource());
return this;
}
private T GetSource()
{
if (_source.TryGetTarget(out var source)) return source;
throw new InvalidOperationException($"{nameof(source)} has been garbage collected.");
}
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return OnReceiveWeakEvent(managerType, sender, e);
}
public virtual bool OnReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType != typeof(PropertyChangedEventManager))
{
return false;
}
var propertyName = ((PropertyChangedEventArgs)e).PropertyName;
Notify(propertyName);
return true;
}
protected void Notify(string propertyName)
{
var source = GetSource();
if (source == null)
{
throw new InvalidOperationException("Confused, received a PropertyChanged event from a source that has been garbage collected.");
}
if (string.IsNullOrEmpty(propertyName))
{
foreach (var handler in _handlers.Values)
{
handler();
}
foreach (var handler in _handlersT.Values)
{
handler(source);
}
}
else
{
if (_handlers.TryGetValue(propertyName, out var handler))
handler();
if (_handlersT.TryGetValue(propertyName, out var handlerT))
handlerT(source);
}
}
public static string GetPropertyNameFromLambda(Expression<Func<T, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression unaryExpression)
{
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
if (memberExpression == null)
throw new ArgumentException("Property name lambda expression needs to be in the form: n = > n.PropertyName");
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
throw new InvalidOperationException("Bug, memberExpression.Member as PropertyInfo cast failed.");
return propertyInfo.Name;
}
}