程序问答   发布时间:2022-06-01  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了了解每个 MVVM 组件大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

如何解决了解每个 MVVM 组件?

开发过程中遇到了解每个 MVVM 组件的问题如何解决?下面主要结合日常开发的经验,给出你关于了解每个 MVVM 组件的解决方法建议,希望对你解决了解每个 MVVM 组件有所启发或帮助;

我知道有一些与此类似的问题,但似乎没有什么能让事情变得有趣。我知道在 MVVM 中有一个模型、视图模型和视图。我会尽量简化这个问题,这样我就可以了解哪些组件包含在哪些组件中。在这个特定的代码中,我希望能够通过顶部栏菜单控制侧栏菜单的可见性。然后,我将为侧边栏菜单创建一个导航,但试图找出哪些地方看起来很困难,但我不知道为什么。

仅供参:如果您尝试重新创建命名空间已更改并且我正在使用 Material Design NuGet 包

这应该是称为 BindableBase 的基础模型(我认为?)该模型包含 INotifyPropertyChanged,但是因为这是一个基础,我不应该在其中包含任何变量,还是应该?

型号:

using System.ComponentModel;
using System.Threading.Tasks;

namespace MainProgram
{
    public abstract class BindableBase : INotifyPropertyChanged
    {

        public String ObjectSender { get; set; }
        
        public BindableBase()
        {
            Task.Run(async () =>
            {             
                OnPropertyChanged(ObjectSender);
            });
        }

        public voID OnPropertyChanged(String sender)
        {
            PropertyChanged(this,new PropertyChangedEventArgs(nameof(sender))); 
        }

        public event PropertyChangedEventHandler PropertyChanged = (sender,E) => { };


    }
}

接下来我们有 viewmodel,它是主视图的许多代码所在。基本上任何将在视图中完成的操作都将放在此处。通常包括 ObservableCollection。我对发生在何处以及为什么发生的事情感到困惑。另外,因为我不太确定如何从这里控制 System.windows.Controls。如果您注意到底部关闭功能工作正常,但尝试获得可见性并没有真正起作用。

查看模型:

using System.windows;
using System.windows.Controls;
using System.windows.input;
using System.linq;
using System.Collections.Generic;

namespace MainProgram
{
    public partial class topMenubarviewmodel : BindableBase
    {

        #region Commands
        public ICommand Close { get; }
        public ICommand ChangeMenuVisibility { get; }
        #endregion

        public topMenubarviewmodel()
        {
            this.Close = new RelayCommand(CloseWindow);
            this.ChangeMenuVisibility = new RelayCommand(ChangedMenuVisibility);
        }


        private voID CloseWindow()
        {
            System.windows.Application.Current.dispatcher.Invoke(() =>
            {
                MainWindow mainWindow = System.windows.Application.Current.windows.OfType<MainWindow>().FirstOrDefault();
                if (mainWindow != null)
                    mainWindow.Close();

            });
            
        }

        private voID ChangedMenuVisibility()
        {

            System.windows.Application.Current.dispatcher.Invoke(() =>
            {
                if (MainWindowviewmodel.MenuVisibility == Visibility.VisiblE)
                    MainWindowviewmodel.MenuVisibility = Visibility.Collapsed;
                

            });
            //MainWindowviewmodel.MenuVisibility = Visibility.Visible;
        }
    }
}

这是绑定视图和视图模型的 vIEw.cs,但如果我通过 DataTemplate 绑定它,我不确定为什么需要它。我不知道是我做了两次还是两者都需要。

查看:

using System.windows.Controls;



namespace MainProgram
{
    /// <sum@R_874_11035@>
    /// Interaction logic for topMenubar.xaml
    /// </sum@R_874_11035@>
    public partial class topMenubar : UserControl
    {

        public topMenubar()
        {
            InitializeComponent();
            var viewmodel = new topMenubarviewmodel();
            this.DataContext = viewmodel;
        }
    }
}

这是视图 topMenubar.xaml 的 XAML 我的所有绑定都是正确的,因为我看到命令正在执行。

<UserControl x:Class="MainProgram.topMenubar"
             xmlns="http://scheR_268_11845@as.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://scheR_268_11845@as.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://scheR_268_11845@as.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://scheR_268_11845@as.microsoft.com/Expression/blend/2008" 
             xmlns:local="clr-namespace:MainProgram"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="50" d:DesignWIDth="800">
    <GrID>
        <GrID.columnDeFinitions>
            <columnDeFinition/>
            <columnDeFinition/>
        </GrID.columnDeFinitions>
        
        <StackPanel GrID.column="0" OrIEntation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <button Height="auto" WIDth="50" Command="{Binding ChangeMenuVisibility}" BACkground="transparent" Style="{Staticresource resourceKey=RoundCorner}" margin="3,1">
                <GrID>
                    <materialDesign:PackIcon BACkground="transparent" Kind="Menu" Height="auto" WIDth="auto"/>
                </GrID>
            </button>
        </StackPanel>

        <StackPanel GrID.column="1" OrIEntation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Stretch">
            <!--="{Binding CloseWindow}"-->
            <button Height="auto" WIDth="50" BACkground="transparent" Style="{Staticresource resourceKey=RoundCorner}" margin="3,1">
                <GrID>
                    <materialDesign:PackIcon BACkground="transparent" Kind="Help" Height="auto" WIDth="auto"/>
                </GrID>
            </button>
            <button Height="auto" WIDth="50" Command="{Binding ClosE}" BACkground="transparent" Style="{Staticresource resourceKey=RoundCorner}" margin="3,1">
                <GrID>
                    <materialDesign:PackIcon BACkground="transparent" Kind="Power" Height="auto" WIDth="auto"/>
                </GrID>
            </button>
        </StackPanel>
    </GrID>
</UserControl>

这是我试图控制其可见性的 XAML,但它不允许我这样做。这是在 MainWindow.xaml 中。我还看到了其他一些代码,允许人们绑定用户控件的当前视图,例如侧栏菜单,但无论我多么努力,我似乎​​都无法让它工作。

 <border Visibility="{Binding MenuVisibility}" CornerRadius="5" borderBrush="Black" borderThickness="3" GrID.column="0"  GrID.Row="2" GrID.rowspan="5" margin="5">
            <ccontrols:DeitoContentControl  x:name="MenuSectionVIEw"  VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
                <ContentControl.ContentTemplate>
                    <DataTemplate DataType="{x:Type viewmodellocal:NavigationMenuviewmodel}">
                        <VIEwlocal:NavigationMenu/>
                    </DataTemplate>
                </ContentControl.ContentTemplate>
               
            </ccontrols:DeitoContentControl> 
        </border>

这是包含绑定变量的 MainWindowviewmodel。 viewmodelBase 派生自 BindableBase。

using System.ComponentModel;
using System.windows;
using System.windows.Controls;

namespace MainProgram
{
    public class MainWindowviewmodel : viewmodelBase,INotifyPropertyChanged
    {
        #region Private Members

        private UserControl _currentFrame;

        private static Visibility _MenuVisibility;
        #endregion



        #region Constructors

        public MainWindowviewmodel()
        {
           
        }

        public UserControl CurrentFrame
        {
            get { return _currentFrame; }
            set
            {
                if (value == _currentFramE)
                    return;
                _currentFrame = value;
                OnPropertyChanged("CurrentFrame");
            }
        }

        public static Visibility MenuVisibility
        {
            get { return _MenuVisibility; }
            set
            {
                if (_MenuVisibility == value)
                    return;
                _MenuVisibility = value;
                //OnPropertyChanged("MenuVisibility");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged = (s,E) => { };

        private voID OnPropertyChanged(String sender)
        {
            PropertyChanged(this,new PropertyChangedEventArgs(nameof(sender)));
        }

      
        #endregion



    }
}

感谢您阅读并提供帮助。如果我需要更改、添加、编辑某些内容,请告诉我。我没有显示完整 MainWindow 的代码,但如果需要我可以。谢谢。

解决方法

现在,我不是 MVVM 的专家,但我过去曾使用过它,所以让我尝试澄清一些事情。对知识渊博的人:请随时纠正我


这应该是称为 BindableBase 的基础模型(我认为?)该模型包含 INotifyPropertyChanged,[...]

由于您没有发布 ViewModelBase 源代码,所以我假设它只是从 BindableBase 继承而来,并且没有添加任何其他内容。在那种情况下,我能想到的分离的唯一原因是允许模型从 BindableBase 继承。
这不是 MVVM 背后的代码思想的一部分,模型应该与任何 ui 逻辑完全分离,因此通常实现 INotifyPropertyChanged 接口。
(顺便说一句,当然可以让模型实现 INotifyPropertyChanged,但是当您试图理解核心 MVVM 概念时,我认为这只会增加混乱)。

如 here 所述,模型本身不包含任何与 UI 交互相关的逻辑,而仅包含您的应用程序试图解决的底层任务所需的数据和逻辑.
我通常是这样想的:

如果我想要同时拥有命令行版本和 UI 应用程序,我会将哪些部分移动到两个版本都可以包含的库中?

答案很可能就是您的模型中应该包含的内容。

如果应用程序的唯一目的是试验 WPF 绑定,则它不需要任何此类逻辑,因此不会有任何模型。


让我们更详细地了解您的 BindableBase 类。我的第一个建议是将它与 ViewModelBase 类合并并让所有视图模型都继承自它。 它的目的是处理围绕 PropertyChanged 事件的所有内容,因此您不必在每个视图模型中都包含该代码,并且除了 PropertyChanged 事件之外,它确实不应该包含任何字段或属性。

OnPropertyChanged 方法应该接收更改属性的名称并调用 PropertyChanged 事件处理程序。请注意,由于 "sender" 运算符,您的实现总是错误地将字符nameof 作为属性名称传递。 这很可能是从未收到您的可见性更改事件的原因。

名称 sender 通常也用于指代触发事件的对象(注意 PropertyChangedEventHandler 委托的第一个参数如何称为 sender并且您正在通过 this)。

此外,您可能还想查看 the CallerMemberName attribute,使用它您不必总是手动指定属性名称。

除此之外,我有点困惑 ObjectSender 属性和您的构造函数的目的是什么。如果我没有遗漏任何东西,当构造函数中的 ObjectSender 运行并且 null 事件不会有任何订阅者时,Task 可能会是 PropertyChanged,所以没有人无论如何都会对那个被触发的事件采取行动。

应用所有这些要点,我们最终得到这样的结果:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    
    public void OnPropertyChanged([CallerMemberName] String propertyName = null)
    {
        PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName)); 
    }
}

接下来是 TopMenuBarViewModel。除了它继承自 BindableBase 而不是 ViewModelBase 之外,它对我来说看起来基本没问题,而且我认为没有理由拥有视图模型 partial

我通常看到实现的命名约定是将所有 ICommand 属性以单词 Command 结尾,这样您就不会遇到与实现方法的名称冲突。

我也不明白为什么 @H_325_13@mainWindowViewModel.Visibility 应该是 static,您可能不知道如何访问它?

TopMenuBarViewModel 带有提议的更改:

public class TopMenuBarViewModel : ViewModelBase
{
    #region Commands

    public ICommand CloseWindowCommand { get; }
    public ICommand ChangeMenuVisibilityCommand { get; }
    
    #endregion
    
    public TopMenuBarViewModel()
    {
        this.CloseWindowCommand = new RelayCommand(CloseWindow);
        this.ChangeMenuVisibilityCommand = new RelayCommand(ChangeMenuVisibility);
    }
    
    private void CloseWindow()
    {
        System.Windows.Application.Current.Dispatcher.Invoke(() =>
        {
            MainWindow mainWindow = System.Windows.Application.Current.Windows
                .ofType<MainWindow>()
                .FirstOrDefault();

            if (mainWindow != null)
                mainWindow.Close();
        });
    }

    private void ChangeMenuVisibility()
    {
        System.Windows.Application.Current.Dispatcher.Invoke(() =>
        {
            // You might want to consider querying the MainWindowViewModel once
            // in the constructor and storing it for future use.
            MainWindow mainWindow = System.Windows.Application.Current.Windows
                .ofType<MainWindow>()
                .FirstOrDefault();

            if(!(mainWindow.DataContext is MainWindowViewModel mainWindowViewModel))
                return; // invalid view model

            switch(mainWindowViewModel.MenuVisibility)
            {
                case Collapsed:
                case Hidden:
                    mainWindowViewModel.MenuVisibility = Visibility.Visible;
                    break;
                case Visible:
                    mainWindowViewModel.MenuVisibility = Visibility.Collapsed;
                    break;
            }
        });
    }
}

您提到要设置 TopMenuBarDataContext 两次,一次在构造函数中,一次使用 DataTemplate。我不确定您所说的“通过 DataTemplate 绑定”是什么意思,该代码似乎丢失了。你的意思是这样吗?

<ContentControl>
    <ContentControl.ContentTemplate>
        <DataTemplate DataType="{x:Type myviewmodels:TopMenuBarViewModel}">
            <myviews:TopMenuBar/>
        </DataTemplate>
    </ContentControl.ContentTemplate>
    <myviewmodels:TopMenuBarViewModel/>
</ContentControl>

如果是,它可能可以简化为这样的东西(已经有一段时间了,所以它可能不完全正确):

<myviews:TopMenuBar>
    <myviews:TopMenuBar.DataContext>
        <myviewmodels:TopMenuBarViewModel/>
    </myviews:TopMenuBar.DataContext>
</myviews:TopMenuBar>

无论哪种方式,XAML 绑定的 DataContext 都应该优先,因此在构造函数中设置它是不必要的,除非您想要一个“后备”实例,以防它没有显式设置。

除此之外,我对 XAML 代码没什么好说的。绑定在我看来都是正确的(如果您最终按照上述建议更改命令名称,则需要调整命令名称)。

如果你还不知道这个:你可以添加属性
d:DataContext="{d:DesignInstance Type=myviewmodels:TopMenuBarViewModel}"
到您的 UserControl 开始标记,这将为您提供数据绑定的 IntelliSense 自动完成。


最后,您的 @H_325_13@mainWindowViewModel。 ViewModelBase 基类的全部意义在于您的具体视图模型不必处理实现 INotifyPropertyChanged(它已经在基类中实现)。 这意味着不需要您的实现的底部部分。

如上所述,我不明白为什么 @H_325_13@menuVisibility 应该是 static

另外,请注意我是如何删除 OnPropertyChanged 的显式字符串参数的。这是通过上面提到的 CallerMemberName 属性实现的。您也可以使用 nameof(CurrentFramE),但请永远手动将其写为字符串文字。一旦您决定重命名其中一个属性并且容易出现拼写错误,这就会中断。

@H_325_13@mainWindowViewModel 带有提议的更改:

public class MainWindowViewModel : ViewModelBase,{
    private UserControl _currentFrame;
    public UserControl CurrentFrame
    {
        get => _currentFrame;
        set
        {
            if (value == _currentFramE)
                return;
            _currentFrame = value;
            OnPropertyChanged();
        }
    }

    private Visibility _menuVisibility;
    public Visibility MenuVisibility
    {
        get => _menuVisibility;
        set
        {
            if (value == _menuVisibility)
                return;
            _menuVisibility = value;
            OnPropertyChanged();
        }
    }
}

哇,这变成了比我原先预期的要多得多的代码审查,但我希望那里仍然有一些对您有用的信息。

这个答案中的所有代码都是凭记忆写的,所以请原谅我可能犯的任何错误。

@H_673_259@

大佬总结

以上是大佬教程为你收集整理的了解每个 MVVM 组件全部内容,希望文章能够帮你解决了解每个 MVVM 组件所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签:组件