Model-View-ViewModel(MVVM)是一种架构设计模式,用于实现用户界面,通过其表示逻辑(其ViewModel)将UI(View)与其数据(Model)分开。
我的 WPF 应用程序顶部有一个带有按钮的栏,允许用户在屏幕之间切换。 XAML: // 费用调查按钮 我的 WPF 应用程序顶部有一个带有按钮的栏,允许用户在屏幕之间切换。 XAML: // FEE SURVEY BUTTON <Menu:Btn Command="{Binding FeeSurveyCommand}" IsChecked="{Binding NavBar.FeeSurvey}" Style="{StaticResource NavButton}"> </Menu:Btn> // PRACTICE SNAPSHOT BUTTON <Menu:Btn Command="{Binding PracticeSnapshotCommand}" IsChecked="{Binding NavBar.PracticeSnapshot}" Style="{StaticResource NavButton}"> </Menu:Btn> // CONTENTS which dynamically changes when users click on the buttons <ContentControl x:Name="Pages" Grid.Column="1" Margin="25,0" Content="{Binding CurrentView}" /> 视图模型: private object _currentView; public object CurrentView { get { return _currentView; } set { _currentView = value; OnPropertyChanged(); } } public ICommand FeeSurveyCommand { get; set; } public ICommand PracticeSnapshotCommand { get; set; } private void FeeSurvey(object obj) { CurrentView = new FeeSurveyVM(); } private void PracticeSnapshot(object obj) { CurrentView = new PracticeSnapshotVM(); } // CONSTRUCTOR public MainViewVM() { FeeSurveyCommand = new RelayCommand(FeeSurvey); PracticeSnapshotCommand = new RelayCommand(PracticeSnapshot); } 因此,当用户单击按钮时,它将创建视图模型的实例(例如 FeeSurveyVM 或 PracticeSnapshotVM)。每个视图模型都与一个视图相关联。渲染新视图时,会调用多个 API。我遇到的问题是当用户切换视图时,API 调用不会中止。假设有人在这些视图之间来回切换,如果 API 调用出现错误(例如 404、400、500 错误),即使我已切换到,也会显示一堆先前访问过的视图的错误消息另一种观点。当视图发生变化时,有没有办法取消/中止这些 API 调用?我一直在寻找解决方案,但没有运气。 这看起来更像是设计错误。您可以尝试这些选项: 防止显示来自用户必须交互的 API 的错误消息,例如通过弹出窗口/消息框。只需添加一个带有输出的 TextBox/RichTextBox 控件,这样用户就会意识到错误,但不会因为弹出窗口而感到沮丧。 您可以禁用允许用户在视图之间切换的 UI 控件,直到 API 调用完成。最好在 API 调用过程中禁用整个视图。显示进度条或通知用户 API 调用期间发生的情况 CancellationTokenSource 应该取消执行 API 调用的异步任务,而不是那些已经调用的任务。因此,在这种情况下用户将观察到错误消息。注意你的设计。 UI 和逻辑。
有人可以解释一下如何在视图模型中获取 KeyUp 事件吗? 我已经尝试过这个: 在我看来: 有人可以解释一下如何在视图模型中获取 KeyUp 事件吗? 我已经尝试过这个: 在我看来: <TextBox .....> <i:Interaction.Triggers> <i:EventTrigger EventName="KeyUp"> <ei:CallMethodAction TargetObject="{Binding}" MethodName="Search"/> </i:EventTrigger> </i:Interaction.Triggers> </TextBox> 在我看来模型: [RelayCommand] private void Search() { // .... } 谢谢你。 我收到错误消息,在我的视图模型上找不到具有正确签名的方法。 我也尝试过 [RelayCommand] private void Search(KeyEventArgs) [RelayCommand] private void Search(EventArgs) [RelayCommand] private void Search(object) 您绑定的命令/方法应该是public。 public void Search() { ... }
我有一个使用 MVVM 模式的 WPF 应用程序,当它忙于执行用户必须等待的操作时,有时必须显示等待光标。感谢本页上的答案组合:d...
有人可以解释一下如何在视图模型中获取 KeyUp 事件吗? 我尝试过: 在我看来? 有人可以解释一下如何在视图模型中获取 KeyUp 事件吗? 我尝试过: 在我看来? <TextBox .....> <i:Interaction.Triggers> <i:EventTrigger EventName="KeyUp"> <ei:CallMethodAction TargetObject="{Binding}" MethodName="Search"/> </i:EventTrigger> </i:Interaction.Triggers> </TextBox> 在我看来模型: [RelayCommand] private void Search() { .... } 谢谢你。 我收到错误消息,指出在我的视图模型上找不到具有正确签名的方法。 我也尝试过 [RelayCommand] private void Search(KeyEventArgs) [RelayCommand] private void Search(EventArgs) [RelayCommand] private void Search(object) 下达命令public。 public void Search() { .... }
所以我的 MainWindow.xaml 上有一个文本框。 所以我的 MainWindow.xaml 上有一个文本框。 <Window x:Class="HelloICommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ... <Grid> <TextBox HorizontalAlignment="Left" Height="23" Margin="337,195,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"> <TextBox.InputBindings> <KeyBinding Command="{Binding }" Key="Enter"></KeyBinding> </TextBox.InputBindings> </TextBox> </Grid> </Window> 正如您所看到的,我想将我的 Enter 键绑定到可以单击 Enter 的位置,它会显示一个消息框,其中包含文本框中的文本。 在我的 MainWindow.cs 中,我像这样设置数据上下文。 public MainWindow() { InitializeComponent(); DataContext = new ServerViewModel(); } 然后我就有了实际的 ServerViewModel 以及其中的其他所有内容 这就是我遇到问题的地方,如何将文本从 TextBox 传递到该方法,以便每次单击 Enter 时都可以看到消息。 class ServerViewModel { private TextBoxCommand textCommand { get; private set; } public ServerViewModel() { textCommand = new TextBoxCommand(SendMessage); } //How do I pass the text from the textbox as a parameter here? public void SendMessage() { MessageBox.Show(""); } } I命令接口 class TextBoxCommand : ICommand { public Action _sendMethod; public TextBoxCommand(Action SendMethod) { _sendMethod = SendMethod; } public bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { } public event EventHandler CanExecuteChanged; } 视图只能绑定到公共属性的命令。这意味着第一步是将您的命令定义为公共(只读)属性: public TextBoxCommand TextCommand { get; } ICommand 接口允许将对象作为参数传递给其执行函数。如果您的命令实现,TextBoxCommand允许传递此参数,只需将该参数添加到您的方法中,将其转换为字符串,然后显示您的消息: private void SendMessage(object parameter) { MessageBox.Show((string)parameter); } 如果您的 TextBoxCommand 不允许传递参数,则像如何实现可重用 ICommand 所示的简单实现即可解决问题。只需将您的 TextBoxCommand 替换为教程中的 DelegateCommand 即可。 要从您的视图中正确执行命令,您现在需要将命令绑定到 TextCommand 属性。第二步是将文本框的文本设置为命令参数。使用此名称,您可以绑定到 Text 属性并将其作为参数传递给您的命令。因此,您需要为文本框命名。这是最小的例子: <TextBox x:Name="yourTextBox> <TextBox.InputBindings> <KeyBinding Key="Enter" Command="{Binding TextCommand}" CommandParameter="{Binding Text, ElementName=yourTextBox}"/> </TextBox.InputBindings> </TextBox>
我有一个 DataGrid,其中包含 XAML 列,如下所示: 我有一个 DataGrid,其中包含 XAML 列: <DataGridTextColumn Header="Time" Binding="{Binding Date, StringFormat='yyyy-MM-dd HH:mm:ss'}" SortMemberPath="Date" SortDirection="Descending" Width="130" CanUserResize="True" /> <DataGridTextColumn Header="Level" Binding="{Binding Level}" Width="60" CanUserResize="True" /> <DataGridTextColumn Header="Source" Binding="{Binding Logger}" Width="150" CanUserResize="True" /> <DataGridTextColumn Header="Message" Binding="{Binding Message}" Width="*" CanUserResize="True" /> 我将其绑定到 ObservableCollection<EalsLogEvent>,其中输入 EalsLogEvent.Date DateTime: public ObservableCollection<EalsLogEvent> LogEvents { get { return _logEvents; } } 网格视图模型使用计时器来刷新自身,网格的一切看起来都很好,除了在应用程序启动时首次加载时。然后,Time 列似乎按降序排序,但实际上是按升序排序。 为了正确排序,我必须单击列标题两次;第一次将顺序更改为升序,现在与列的内容匹配。第二次单击列标题会将其排序顺序更改回降序,这次它对列内容进行正确排序,即降序。 如果我在 _logEvents 刷新时使用 LINQ 对集合进行排序,我会丢失用户通过单击列标题为该列设置的任何顺序。如果我必须让视图告诉模型 LINQ 排序应该使用哪个顺序,那就有点糟糕了。 您可以在 XAML 中使用 CollectionViewSource 来定义默认排序。 假设我们有一个视图模型: public class ViewModel : INotifyPropertyChanged { public ObservableCollection<Item> Items { get; private set; } } 我们可以为 CollectionView 集合创建自定义 Items: <Window xmlns:l="clr-namespace:YourNamespace" xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"> <Window.DataContext> <l:ViewModel/> </Window.DataContext> <Window.Resources> <CollectionViewSource Source="{Binding Items}" x:Key="GridItems"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="Date" Direction="Descending"/> </CollectionViewSource.SortDescriptions> </CollectionViewSource> </Window.Resources> <DataGrid ItemsSource="{Binding Source={StaticResource GridItems}}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="Time" Binding="{Binding Date, StringFormat='yyyy-MM-dd HH:mm:ss'}" Width="130" CanUserResize="True" /> <DataGridTextColumn Header="Level" Binding="{Binding Level}" Width="60" CanUserResize="True" /> <DataGridTextColumn Header="Source" Binding="{Binding Logger}" Width="150" CanUserResize="True" /> <DataGridTextColumn Header="Message" Binding="{Binding Message}" Width="*" CanUserResize="True" /> </DataGrid.Columns> </DataGrid> </Window> 使用这种方法,您的底层源集合(本例中为Items)将不会受到影响,排序仅发生在视图中。 正如您可以在 MSDN 中阅读的那样: 您可以将集合视图视为绑定顶部的层 源集合,允许您导航和显示 基于排序、过滤和分组查询的集合,全部无需 必须操纵底层源集合本身。如果 源集合实现了 INotifyCollectionChanged 接口, CollectionChanged 事件引发的更改将传播到 意见。 您还应该注意以下事项: 所有集合都有一个默认的 CollectionView。 WPF 始终绑定到 视图而不是集合。如果直接绑定到集合, WPF 实际上绑定到该集合的默认视图。 因此,使用 CollectionViewSource,您只需为您的集合定义一个自定义视图。 您应该在视图模型中创建 2 个属性: private ObservableCollection<EalsLogEvent> logEvents = new ObservableCollection<EalsLogEvent>(); private ICollectionView logEventsView; public ObservableCollection<EalsLogEvent> LogEvents { get { return this.logEvents; } set { this.SetProperty(ref this.logEvents, value); } } public ICollectionView LogEventsView { get { if (this.logEventsView == null) { this.logEventsView= CollectionViewSource.GetDefaultView(this.LogEvents); this.logEventsView.SortDescriptions.Add(new SortDescription("Time", ListSortDirection.Descending)); } return this.logEventsView; } } 将 DataGrid 绑定到 LogEventsView,并使用 LogEvents 添加或删除项目。 这允许默认排序和用户排序。
如何将 Control 的内容属性作为 CommandParameter WPF 传递
我有一个 WPF 项目,有 19 个单选按钮。我想设置每个命令属性。 为了分隔所有命令操作,我想将 RadioButton Content 属性作为 CommandParameter 发送。 我尝试
是否可以将 Collectionview 中的数组与 ObservableCollection 绑定?
我是新来的,也是新的 .Net Maui 开发人员。我使用 API 并通过 GET 请求接收要处理的数据。我收到的数据由包含不同数量项目的收据组成
我正在使用域层实现 MVVM android 应用程序。它采用标准方式: Activity -> ViewModel -> UseCase(带有注入的存储库) 我有一个架构问题想问
我有一个绑定可观察集合的数据网格。当我单击添加新按钮时,我会将新行添加到集合中。我如何实用地滚动到新行。 数据网格 XMAL 我有一个绑定可观察集合的数据网格。当我单击添加新按钮时,我会将新行添加到集合中。我如何实用地滚动到新行。 数据网格XMAL <DataGrid SelectedIndex="{Binding SelectedIntex}" IsEnabled="{Binding IsKeySet}" CanUserDeleteRows="False" CanUserAddRows="False" Name="dgwTemplateDetails" SelectionMode="Single" ItemsSource="{Binding OrderTemplateList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedOrderTemplate}" IsReadOnly="False" AutoGenerateColumns="False" Width="auto"> <DataGrid.Columns> <DataGridTextColumn Header="Srl No" Visibility="Hidden" Binding="{Binding SrlNo}"/> <DataGridTextColumn Header="Act Code" Width="75" Binding="{Binding ActCode, UpdateSourceTrigger=PropertyChanged}"/> <DataGridTextColumn Header="Act Name" Width="275" Binding="{Binding ActName, UpdateSourceTrigger=PropertyChanged}"/> <DataGridTextColumn Header="No. Of Days" Width="75" Binding="{Binding NoOfDays, UpdateSourceTrigger=PropertyChanged}"/> <DataGridCheckBoxColumn Header="Is Cutting" Width="75" Binding="{Binding IsCutSelected, UpdateSourceTrigger=PropertyChanged}" /> </DataGrid.Columns> </DataGrid> VM 添加行功能 if (ValidateHeader()) { if (OrderTemplateList == null) this.OrderTemplateList = new ObservableCollection<EventManagementTemplate>(); EventManagementTemplate obJEvent = new EventManagementTemplate(); obJEvent.BuyerCode = this.BuyerCode; this.OrderTemplateList.Add(obJEvent); int no = 1; this.OrderTemplateList.ToList().ForEach(m => m.SrlNo = no++); } 您需要执行以下操作: 为您的 dataGrid 命名,以便您可以在代码隐藏文件(与包含数据网格的 xaml 文件配对的 .cs 文件)中访问它 向您的视图模型添加一个委托,以供 AddRow 函数调用 - 该委托应将您想要滚动到视图中的对象作为参数。 让包含数据网格的背后代码订阅委托==>基本上,背后的代码是提供对视图模型的回调。回调后面的代码将是滚动到新项目的代码。 回调应调用数据网格的 ScrollIntoView 函数 (http://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid.scrollintoview(v=vs.110).aspx) 在视图模型的添加行函数结束时(添加项目后),使用要滚动到视图中的项目调用委托。 示例代码: public class MyViewModel { // declare the delegate signature... delegate void ScrollIntoViewDelegateSignature(EventManagementTemplate objEvent); // create a pointer to the delegate that can be set by the code behind... public ScrollIntoViewDelegateSignature ScrollIntoView {get; set;} protected AddRow() { .. your code here // call the delegate... if (ScrollIntoView != null) ScrollIntoView(objEvent) } } public class MyControlOrWindowThatContainsDataGrid : UserControl/ChildWindow/Page { public void Initialize() { ...your code here // set the scrollIntoView delegate... myViewModel.ScrollIntoView = ScrollIntoView; } // create a ScrollIntoView function // ==> the return value and parameters MUST match the delegate signature public void ScrollIntoView(EventManagementTemplate objEvent) { myDataGrid.ScrollIntoView(objEvent); } } 参考 http://msdn.microsoft.com/en-us/library/900fyy8e.aspx 我认为最好使用事件来做到这一点,但这更复杂。 以下是有关如何使用事件的 msdn 文档:http://msdn.microsoft.com/en-us/library/awbftdfh.aspx 您可以为 DataGrid 创建行为。在此代码中,仅当新行添加到集合中时,它才会滚动到网格的最后一行。您可以根据任何条件修改它。有时需要添加一些延迟。如果不需要,您可以排除。 public class AutoScrollToEndBehavior : Behavior<DataGrid> { private DateTime _lastCollectionChangedTime = DateTime.MinValue; protected override void OnAttached() { base.OnAttached(); SubscribeToCollectionChanged(); } protected override void OnDetaching() { base.OnDetaching(); UnsubscribeFromCollectionChanged(); } private void SubscribeToCollectionChanged() { if (AssociatedObject.Items is ICollectionView collectionView) { collectionView.CollectionChanged += OnCollectionChanged; } } private void UnsubscribeFromCollectionChanged() { if (AssociatedObject.Items is ICollectionView collectionView) { collectionView.CollectionChanged -= OnCollectionChanged; } } private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { _lastCollectionChangedTime = DateTime.Now; Task.Delay(100).ContinueWith(_ => { if ((DateTime.Now - _lastCollectionChangedTime).TotalMilliseconds > 100) { ScrollToEnd(); } }, TaskScheduler.FromCurrentSynchronizationContext()); } } private void ScrollToEnd() { if (AssociatedObject.Items.Count > 0) { var lastItem = AssociatedObject.Items[AssociatedObject.Items.Count - 1]; AssociatedObject.Dispatcher.Invoke(() => { AssociatedObject.ScrollIntoView(lastItem); }); } } } 在 XAML 中, 在 datagrid () 的结束标记上方添加以下代码 <b:Interaction.Behaviors> <behavior:AutoScrollToEndBehavior/> </b:Interaction.Behaviors> </DataGrid> 在 DataGrid 中添加项目后尝试此操作: dgwTemplateDetails.ScrollIntoView(obJEvent); 此行将滚动到当前添加的元素。
Jetpack Compose 如何从视图模型收集流并在可组合项中对它们进行操作?
视图模型: 类 RulesViewModel : ViewModel() { 私有 val _sharedFlow = MutableSharedFlow() val shareFlow = _sharedFlow.asSharedFlow() 密封类
Не понимаю,почему при добавлении элемента в список программа не считывает изменения и не вызввает метод 观察。 数据类 ListOfItems( var 列表:MutableList ) 数据类项目( ...
我想问一下如何在.net Maui 应用程序中进行条件绑定。例如 如果我有课 公开课A级 { 公共字符串 Property1{get;set;} 公共字符串 Property2{get;set...
ViewModel 结构在单独的 Jetpack Compose 可组合项中调用时不维护变量值
在 DiscoverCountries 可组合页面中,有一个可组合按钮,用于更新 ContinentsViewModel ViewModel() 类的 selectedContinent 字符串成员的值。我正在尝试使用...
如何通过 MVVM 为 DataGrid ItemSource 设置过滤器
我有一个 DataGrid 绑定到 XAML 中的 CollectionViewSource。 我有一个 DataGrid 绑定到 XAML 中的 CollectionViewSource。 <Window.Resources> <local:MainWindowViewModel x:Key="ViewModel"/> <CollectionViewSource x:Key="cvsEntries" Source="{Binding LogEntriesStore, Source={StaticResource ViewModel}}"/> </Window.Resources> LogEntriesStore 是一个 ObservableCollection (LogEntry 是一个 DTO,在本次讨论中并不重要) DataGrid 声明为: <DataGrid AutoGenerateColumns="False" Margin="0" Name="dataGrid1" ItemsSource="{Binding Source={StaticResource cvsEntries}}" IsReadOnly="True"> 现在,我在这个 DataGrid 中的各个单元格上都有上下文菜单,以启动过滤请求。右键单击一个单元格,然后选择筛选器来筛选所有行,并仅显示此特定值。 MVVM 获取要过滤的请求,但现在是棘手的一点。如何在 CollectionViewSource 上设置过滤器? (顺便说一句 - 这就像在公园里用 Silverlight 散步一样轻松 PagedCollectionView,但 WPF 中似乎不提供这种功能,是吗?) 非常简单。您只需将集合视图移动到视图模型中即可: 在MainWindowViewModel中定义类型为ICollectionView的属性: public ICollectionView LogEntriesStoreView { get; private set; } 初始化 LogEntriesStore 属性后,您需要使用以下代码初始化 LogEntriesStoreView 属性: LogEntriesStoreView = CollectionViewSource.GetDefaultView(LogEntriesStore); 然后您需要从 XAML 中删除 CollectionViewSource 并修改 ItemsSource 绑定以指向新创建的集合视图属性: <DataGrid AutoGenerateColumns="False" Margin="0" Name="dataGrid1" ItemsSource="{Binding LogEntriesStoreView, Source={StaticResource ViewModel}}" IsReadOnly="True"> 就是这样。现在您可以访问视图模型内的集合视图,您可以在其中修改过滤器。 您的问题有多种解决方案,但在我看来,最好的解决方案是仅使用标准 WPF DataGrid 控件的样式,而不发明新的继承 DataGird 类型或依赖于其他第三方控件。以下是我找到的最佳解决方案: 选项 1:我个人使用:自动 WPF 工具包 DataGrid 过滤 选项 2:Microsoft WPF DataGrid 自动筛选 我还有一个问题。我也这样做。但是我现在如何处理 ObservableCollection 中模型的属性更改呢?无论如何,它不会在模具视图中刷新。
如何让PasswordBox在错误验证时显示错误消息(并删除红色框)?
有几个关于错误验证的线程。然而,如果要将我的代码组合在一起,我就会失败。我对 WPF 的理解还很遥远。因此,您的帮助、建议和审查我...
无法使用 CommunityToolkit.Mvvm 在视图模型中使用 ICommand 属性
在我的视图模型中,我想使用 CommunityToolkit.Mvvm 中的源生成器,但由于某种原因,我似乎无法在我的操作方法中使用 [ICommand] 属性。 我得到的错误是: 无法应用...
Avalonia MVVM Toolkit 源生成器调用方法或引发事件未完全正常工作
关于 [ObservableCollection] 和 [ObservableProperty] 行为差异的问题 我曾在 WPF 工作过一段时间,现在我要搬到 Avalonia。很多事情都会按预期进行......
这应该很简单,但是它会让 VS2008 陷入严重的循环。 我正在尝试使用 MVVM 的 WPF,虽然我已经开发了大约 15 年,并且有一个比较,但我完全是新手。科学。
如何将DataGridColumn作为命令参数传递? (WPF)
我的 DataGrid 有一些具有相同模板的列。 我的 DataGrid 有一些具有相同模板的列。 <DataTemplate x:Key="ButtonCellTemplate"> <Button Content="TestButton" Command="{Binding TestCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridColumn}}" /> </DataTemplate> 它不起作用,我知道为什么。 如何将 DataGridColumn(或 DataGridColumnHeader)作为命令参数传递? 换句话说,我需要知道在 ViewModel 中从哪一列调用该命令。 如果我们不涉及在 ViewModel 中接收此类信息从根本上来说是概念上错误的问题,那么这可以实现,但方式略有不同。 您对 DataGrid 可视化树存在误解。它是 ItemsControl 的继承者。因此,每一行旨在显示源集合的一个元素。单元格放置在一行中以表示该行的一个或另一个元素 - 通常是该元素的属性之一。这样的列实际上并不存在。这些只是细胞从中接收所需数据的容器。因此,该列不是单元格的祖先,更不用说其内容了。 但是单元格具有与其所在列进行通信的属性。你可以用它。 <DataTemplate x:Key="ButtonCellTemplate"> <Button Content="TestButton" Command="{Binding TestCommand}" CommandParameter="{Binding Column, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}}"/> </DataTemplate>