WPF使用MVVMLight學習入門


參考文檔

共四篇入門介紹  MvvmLight框架使用入門(一) - 樓上那個蜀黍 - 博客園 (cnblogs.com)

官方使用文檔  Introduction to the MVVM Toolkit - Windows Community Toolkit | Microsoft Docs

 

DLL介紹

  • Microsoft.Toolkit.Mvvm.ComponentModel
    • ObservableObject    該類實現了INotifyPropertyChanged接口,定義了一個可通知的對象基類,供ViewModelBase繼承   摘要:  一個對象的基類,其屬性必須是可觀察的
    • ObservableRecipient   可觀察對象的基類,它也充當消息的接收者  這個類是ObservableObject的擴展,它也提供了使用Microsoft.Toolkit.Mvvm.Messaging.IMessenger類型的內置支持。  
    • 是一個基類,實現了INotifyDataErrorInfo接口,為驗證向其他應用程序模塊公開的屬性提供了支持。 它也繼承了ObservableObject,所以它也實現了INotifyPropertyChanged和INotifyPropertyChanging。 它可以作為所有需要支持屬性更改通知和屬性驗證的對象的起點
  • Microsoft.Toolkit.Mvvm.DependencyInjection
    • Ioc一種便於系統使用的類型。 IServiceProvider類型。 ioc提供了在單例、線程安全的服務提供者實例中配置服務的能力,然后可以使用該實例來解析服務實例。 使用此特性的第一步是聲明一些服務
  • Microsoft.Toolkit.Mvvm.Input
  • Microsoft.Toolkit.Mvvm.Messaging
    • IMessenger  提供在不同對象之間交換消息能力的類型的接口 , 還可以將消息發送到由令牌唯一標識的特定通道,並在應用程序的不同部分中使用不同的信使
    • WeakReferenceMessenger
      • 消息接口的實現  備注:imessenger實現使用弱引用來跟蹤已注冊的收件人,所以當不再需要收件人時,
      • 不需要手動注銷他們該類型將自動執行內部調整時完整GC調用集合,所以手動調用Cleanup沒有必要確保平均內部數據結構盡可能的削減和緊湊。 注意:在。net Framework中運行時,由於應用程序域卸載問題,不支持此功能。  
    • StrongReferenceMessenger
      • 消息接口的實現  備注:imessenger實現使用強引用來跟蹤已注冊的收件人,所以當他們不再需要時,必須手動注銷他們。
    • IRecipient<TMessage>  用於聲明特定消息類型注冊的接收方的接口。
    • MessageHandler<TRecipient, TMessage>
  • Microsoft.Toolkit.Mvvm.Messaging.Messages

 MVVM簡單使用方法

 安裝包

 

 

 添加視圖模型

    public class MainWindowViewModel : ObservableObject
    {
        private string title;

        public string Title
        {
            get { return title; }
            set { SetProperty(ref title, value); }
        }

        public ICommand ChangeTitleCommand { get; set; }



        private void ChangeTitle()
        {
            Title = "Hello MvvmLight";
        }
        public MainWindowViewModel()
        {
            Title = "Hello World";
            ChangeTitleCommand = new RelayCommand(ChangeTitle);
        }
    }

 

添加視圖模型Locator

public class ViewModelLocator
{
    /// <summary>
    /// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
    /// </summary>
    internal static  IServiceProvider Services { get; private set; }
    public ViewModelLocator()
    {
        ConfigureServices();
    }
    /// <summary>
    /// Configures the services for the application.
    /// </summary>
    private static IServiceProvider ConfigureServices()
    {
        
        var services = new ServiceCollection();
        services.AddSingleton<MainWindowViewModel>();
        Services = services.BuildServiceProvider();
        Ioc.Default.ConfigureServices(Services);
        return Services;
    }
    public MainWindowViewModel MainVM=> Services.GetService<MainWindowViewModel>();
}

 

XAML配置

 

 

 主窗體xaml

    <Window.DataContext>
        <Binding Path="MainVM" Source="{StaticResource Locator}"/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Height="50" Text="{Binding Title}"></TextBlock>
        <Button Height="50" Grid.Row="1" Command="{Binding ChangeTitleCommand}"></Button>
    </Grid>

數據綁定的一個備注     利刃 MVVMLight 4:綁定和綁定的各種使用場景 - Hello-Brand - 博客園 (cnblogs.com)

值驗證ObservableValidator

    //參考鏈接  https://www.cnblogs.com/AtTheMoment/p/15676984.html
    public class NumberAttribute : ValidationAttribute
    {
        public NumberAttribute()
        {

        }
        public override string FormatErrorMessage(string name)
        {
            return base.FormatErrorMessage(name);
        }
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
                return new("內容為空!");
            if (!int.TryParse(value.ToString(), out var number))
                return new("年齡非法");
            else if (number <= 0 || number > 100)
                return new($"{number}是非法年齡");

            return ValidationResult.Success;
        }
    }

    public class ValidatorVM : ObservableValidator
    {

        private string name;

        [Required(AllowEmptyStrings =false,ErrorMessage ="名字不可為空")]
        [MaxLength(6,ErrorMessage ="長度不能大於6")]
        public string Name
        {
            get { return name; }
            set { SetProperty(ref name, value, true); }
        }

        private int age;


        [Number]
        public int Age
        {
            get { return age;}
            set { SetProperty(ref age, value, true); }
        }
    }

 

前台代碼

 <TextBox Height="50" Text="{Binding VM.Age}">
                <TextBox.Style>
                    <Style>
                        <Setter Property="Validation.ErrorTemplate">
                            <Setter.Value>
                                <ControlTemplate>
                                    <StackPanel>
                                        <AdornedElementPlaceholder/>
                                        <ItemsControl ItemsSource="{Binding}">
                                            <ItemsControl.ItemTemplate>
                                                <DataTemplate>
                                                    <TextBlock Foreground="Red" Text="{Binding ErrorContent}"/>
                                                </DataTemplate>
                                            </ItemsControl.ItemTemplate>
                                        </ItemsControl>
                                    </StackPanel>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </TextBox.Style>
            </TextBox>

 

 或者參考  https://www.cnblogs.com/wzh2010/p/6518834.html

 命令綁定修改為

ChangeTitleCommand = new RelayCommand(ChangeTitle, CanExecute);
       private bool CanExecute()
        {
            return !VM.HasErrors;
        }

 

完整的模型代碼如下

public class MainWindowViewModel : ObservableObject
    {
        private string title;

        public string Title
        {
            get { return title; }
            set { SetProperty(ref title, value); }
        }

        public ICommand ChangeTitleCommand { get; set; }

        private ValidatorVM _VM;

        public ValidatorVM VM
        {
            get { return _VM; }
            set { _VM = value; }
        }



        private void ChangeTitle()
        {
            Title = "Hello MvvmLight";
        }
        public MainWindowViewModel(IServiceProvider service)
        {
            _VM = service.GetRequiredService<ValidatorVM>();
            Title = "Hello World";
            //ChangeTitleCommand = new RelayCommand(ChangeTitle);

            ChangeTitleCommand = new RelayCommand(ChangeTitle, CanExecute);
        }

        private bool CanExecute()
        {
            return !VM.HasErrors;
        }
    }

 

 帶參數命令

RelayCommand = new RelayCommand<string>(obj => Title = "帶參數的命令" + obj);

 

將參數包裝成依賴屬性,這種方式不推薦,太麻煩

    /// <summary>
    /// 一種方式就是將 UserParam類 改成 支持具有依賴屬性的對象
    /// </summary>
    public class UserParam:FrameworkElement
    {
        public int Age
        {
            get { return (int)GetValue(AgeProperty); }
            set { SetValue(AgeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Age.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AgeProperty =
            DependencyProperty.Register("Age", typeof(int), typeof(UserParam), new PropertyMetadata(0, CustomProperty, CustomValidateValueCallback));

        /// <summary>
        /// 屬性值驗證回調方法
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static object CustomValidateValueCallback(DependencyObject d, object baseValue)
        {
            return baseValue;
        }
        /// <summary>
        /// 屬性值更改回調方法
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void CustomProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
          
        }
    }

 

綁定示例 https://www.cnblogs.com/wzh2010/p/6607702.html

<StackPanel DockPanel.Dock="Left" Width="240">
                            <Button Command="{Binding PassArgObjCmd}"  Content="傳遞多個參數" Height="23" HorizontalAlignment="Left" Width="100">
                                <Button.CommandParameter>
                                    <model:UserParam UserName="{Binding ElementName=ArgStrFrom,Path=Text}" UserPhone="88888888" UserAdd="地址" UserSex="男" ></model:UserParam>
                                </Button.CommandParameter>
                            </Button>
  </StackPanel>

 

轉換器傳遞參數

模型定義如下

    /// <summary>
    /// 一種方式就是將 UserParam類 改成 支持具有依賴屬性的對象
    /// </summary>
    public class UserParam
    {
        public string UserName { get; internal set; }
        public string UserSex { get; internal set; }
        public string UserPhone { get; internal set; }
        public string UserAdd { get; internal set; }
    }

    public class UserInfoConvert : IMultiValueConverter
    {
        /// <summary>
        /// 對象轉換
        /// </summary>
        /// <param name="values">所綁定的源的值</param>
        /// <param name="targetType">目標的類型</param>
        /// <param name="parameter">綁定時所傳遞的參數</param>
        /// <param name="culture"><系統語言等信息</param>
        /// <returns></returns>
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (!values.Cast<string>().Any(text => string.IsNullOrEmpty(text)) && values.Count() == 4)
            {
                UserParam up = new UserParam() { UserName = values[0].ToString(), UserSex = values[1].ToString(), UserPhone = values[2].ToString(), UserAdd = values[3].ToString() };
                return up;
            }

            return null;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

 

ViewModel中添加屬性

        private void ExcuteParam(UserParam obj)
        {
            userParam = obj;
        }

        public IRelayCommand<UserParam> RelayCommand { get;  }

        private UserParam userParam;

        public UserParam UserParam
        {
            get { return userParam; }
            set { SetProperty(ref userParam, value); }
        }
RelayCommand = new RelayCommand<UserParam>(ExcuteParam);

 

前台頁面代碼

名稱空間引入

xmlns:convert="clr-namespace:MvvmSample.Core.ViewModels"
     <StackPanel Margin="10,0,0,50">
                <TextBlock Text="動態參數傳遞" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock>
                <StackPanel Orientation="Horizontal" >
                    <StackPanel Orientation="Vertical" Margin="0,0,10,0" >
                        <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
                            <TextBlock Text="姓名" Width="80" ></TextBlock>
                            <TextBox x:Name="txtUName" Width="200" />
                        </StackPanel>
                        <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
                            <TextBlock Text="電話" Width="80" ></TextBlock>
                            <TextBox x:Name="txtUPhone" Width="200" />
                        </StackPanel>
                        <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
                            <TextBlock Text="地址" Width="80"></TextBlock>
                            <TextBox x:Name="txtUAdd" Width="200"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
                            <TextBlock Text="性別" Width="80" ></TextBlock>
                            <TextBox x:Name="txtUSex" Width="200" />
                        </StackPanel>
                    </StackPanel>

                    <StackPanel>
                        <StackPanel.Resources>
                            <convert:UserInfoConvert x:Key="uic"/>
                        </StackPanel.Resources>
                        <Button Content="點擊傳遞" Command="{Binding RelayCommand}">
                            <Button.CommandParameter>
                                <MultiBinding Converter="{StaticResource uic}">
                                    <Binding ElementName="txtUName" Path="Text"/>
                                    <Binding ElementName="txtUSex" Path="Text"/>
                                    <Binding ElementName="txtUPhone" Path="Text"/>
                                    <Binding ElementName="txtUAdd" Path="Text"/>
                                </MultiBinding>
                            </Button.CommandParameter>
                        </Button>
                    </StackPanel>

                    <StackPanel Width="240" Orientation="Vertical" Margin="10,0,0,0" >
                        <TextBlock Text="{Binding UserParam.UserName,StringFormat='姓名:\{0\}'}" ></TextBlock>
                        <TextBlock Text="{Binding UserParam.UserPhone,StringFormat='電話:\{0\}'}" ></TextBlock>
                        <TextBlock Text="{Binding UserParam.UserAdd,StringFormat='地址:\{0\}'}" ></TextBlock>
                        <TextBlock Text="{Binding UserParam.UserSex,StringFormat='性別:\{0\}'}" ></TextBlock>
                    </StackPanel>
                </StackPanel>
            </StackPanel>

 

傳遞事件參數--拖拽上傳

添加包

 

 名稱空間引用

xmlns:behav="http://schemas.microsoft.com/xaml/behaviors"
            <StackPanel Margin="10,0,0,50">
                <TextBlock Text="傳遞原事件參數" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock>
                <DockPanel x:Name="PassEventArg" >
                    <StackPanel DockPanel.Dock="Left" Width="240" Orientation="Horizontal" >
                        <Border BorderBrush="Red" BorderThickness="1" >
                            <TextBlock Width="100" Height="50"  Text="拖拽上傳" TextAlignment="Center" FontSize="18" AllowDrop="True" >
                            <behav:Interaction.Triggers>
                                <behav:EventTrigger EventName="Drop">
                                    <behav:InvokeCommandAction Command="{Binding DragCommand}" PassEventArgsToCommand="True"/>
                                </behav:EventTrigger>
                            </behav:Interaction.Triggers>
                            </TextBlock>
                        </Border>
                    </StackPanel>
                    <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal">
                        <TextBlock Text="{Binding FileAdd,StringFormat='獲取地址:\{0\}'}" ></TextBlock>
                    </StackPanel>
                </DockPanel>
            </StackPanel>

 

后台代碼

        public IRelayCommand<DragEventArgs> DragCommand { get; set; }
        private string _FileAdd;

        public string FileAdd
        {
            get { return _FileAdd; }
            set { SetProperty(ref _FileAdd, value); }
        }

        private void ExecuteDrop(DragEventArgs obj)
        {

            FileAdd = ((System.Array)obj.Data.GetData(System.Windows.DataFormats.FileDrop)).GetValue(0).ToString();
        }

DragCommand = new RelayCommand<DragEventArgs>(ExecuteDrop);

 

控件的事件轉換為命令

  <StackPanel Margin="10,0,0,50">
                <TextBlock Text="事件轉命令執行" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock>
                <DockPanel x:Name="EventToCommand" >
                    <StackPanel DockPanel.Dock="Left" Width="240" Orientation="Horizontal" >
                        <ComboBox Name="combox" Width="130" ItemsSource="{Binding UserParamList}" DisplayMemberPath="UserName" SelectedValuePath="UserSex">
                            <behav:Interaction.Triggers>
                                <behav:EventTrigger EventName="SelectionChanged">
                                    <behav:InvokeCommandAction Command="{Binding SelectCommand}"/>
                                </behav:EventTrigger>
                            </behav:Interaction.Triggers>
                        </ComboBox>
                    </StackPanel>
                    <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal">
                        <TextBlock Text="{Binding ElementName=combox, Path=SelectedIndex}" ></TextBlock>
                    </StackPanel>
                </DockPanel>
            </StackPanel>

 

后台代碼

        public ObservableCollection<UserParam> UserParamList { get; set; }
        public IRelayCommand SelectCommand { get; }
        private  void OnSelectCommand()
        {
          
        }

 

 

數據初始化

            UserParamList = new()
            {
                new() { UserName = "張三", UserSex = "1" },
                new() { UserName = "李四", UserSex = "2" },
                new() { UserName = "王五", UserSex = "3" },
                new() { UserName = "趙六", UserSex = "4" },
            };
            SelectCommand=new RelayCommand(OnSelectCommand);

 

在多線程和調度中使用

異步調用不卡UI界面

public IAsyncRelayCommand AsyncRelayCommand { get; }
AsyncRelayCommand = new AsyncRelayCommand(() => Task.Run(() => { Thread.Sleep(8000); Title = "我執行完了"; }));

 

Messenger

 

 

 注冊消息的簡單模式

參數說明:

<string, string>  消息類型,令牌類型

this為消息的接收者實例,“AAA”為消息令牌,

MessageHandler:(sender,message)==>sender為接收消息的收件人,message接收到的消息

            WeakReferenceMessenger.Default.Register<string, string>(this, "AAA", (recipient, message) =>
            {
                MessageBox.Show($"MessagePage 接收到了消息: {message};\n消息的收件人類型為{recipient.GetType()};");
            });

不帶令牌的消息注冊

            WeakReferenceMessenger.Default.Register<string>(this, (recipient, message) =>
            {
                if (recipient is MessagePage)//如果是發送給我的
                {
                    MessageBox.Show($"MessagePage 接收到了消息: {message};\n消息的收件人類型為{recipient.GetType()};");
                }
            });

 

備注:同一收件人只能注冊一種消息類型

 

發送字符串消息的兩種形式

            WeakReferenceMessenger.Default.Send( DateTime.Now.ToString("HH:mm:ss")+"發送了字符串消息");
            WeakReferenceMessenger.Default.Send( DateTime.Now.ToString("HH:mm:ss") +"發送了令牌消息,標識符為AAA", "AAA");

 

備注:不帶令牌的形式的消息不會發送給注冊了令牌的接收者,帶令牌的形式,不會發送給沒有注冊令牌的接收者

自動注冊接收消息

備注:需要將isactive設置為true;以下實例將接收到所有字符串類型的消息

        public class MessagePageViewModel : ObservableRecipient, IRecipient<string>
        {
            public MessagePageViewModel()
            {
                this.IsActive = true;
            }
            public void Receive(string message)
            {
                MessageBox.Show($"MessagePage 接收到了消息: {message};消息令牌為:AAA;\n消息的收件人類型為{this.GetType()};");
            }
        } 
ObservableRecipient的方法說明:發送屬性改變的消息,如果想要使用自定義的令牌,可以重寫該方法
protected virtual void Broadcast<T>(T oldValue, T newValue, string? propertyName)

 

注冊接收屬性改變的消息

            WeakReferenceMessenger.Default.Register<PropertyChangedMessage<string>>(this, (recipient, message) =>
            {
                if (recipient is MessagePage)
                {
                    MessageBox.Show($"發件人: {message.Sender};屬性名:{message.PropertyName}");
                }
               
            });

 

 

請求消息

注冊請求消息

            WeakReferenceMessenger.Default.Register<RequestMessage<string>>(this, (recipient, message) =>
            {
                if (recipient is MessagePage)
                {
                    message.Reply("我已經接收到了你的請求消息,這是我的答復");
                }
            });

 

發送請求,並接收答復

var name = WeakReferenceMessenger.Default.Send<RequestMessage<string>>();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM