简体   繁体   English

将 ViewModel 传递给 UserControl

[英]Pass ViewModel to UserControl

MySpecialView is a complex image control, I would like to reuse it from different views and pass its ViewModel as in this example. MySpecialView是一个复杂的图像控件,我想从不同的视图中重用它并像在这个例子中一样传递它的ViewModel

MainWindow.xaml主窗口.xaml

<Window x:Class="YouBug.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:YouBug"
    mc:Ignorable="d"
    DataContext="{Binding MainViewModel}"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <local:MySpecialView ViewModel="{Binding MySpecialViewModel}"></local:MySpecialView>
</Grid>

MainViewModel主视图模型

    public class MainViewModel
{
    public MySpecialViewModel MySpecialViewModel { get; set; }

    public MainViewModel()
    {
        MySpecialViewModel = new MySpecialViewModel();
        //gets not displayed!
        Task.Run(() => MySpecialViewModel.changeImage(5000, "C:\\Users\\user\\Pictures\\Capture.PNG"));
    }
}

MySpecialView.xaml MySpecialView.xaml

<UserControl x:Class="YouBug.MySpecialView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:YouBug"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <Image Source="{Binding ImageSource}" />
</Grid>

MySpecialView.xaml.cs MySpecialView.xaml.cs

    public partial class MySpecialView : UserControl
{
    public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(MySpecialViewModel), typeof(MySpecialView), new FrameworkPropertyMetadata(new MySpecialViewModel(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public MySpecialViewModel ViewModel { get { return (MySpecialViewModel)GetValue(ViewModelProperty); } set { SetValue(ViewModelProperty, value); } }

    public MySpecialView()
    {
        DataContext = ViewModel;
        InitializeComponent();
    }
}

MySpecialViewModel我的特殊视图模型

    public class MySpecialViewModel : ViewModelBase
{
    public BitmapSource imageSource { get; set; }
    public BitmapSource ImageSource { get { return imageSource; }
        set { if (value != imageSource)
            {
                imageSource = value; RaisePropertyChanged("ImageSource");
            }
        } }

    public MySpecialViewModel()
    {
        //gets displayed
        ImageSource = new BitmapImage(new Uri("C:\\Users\\user\\Pictures\\test.jpg"));

        //gets displayed aswell
        Task.Run(() => changeImage(10000, "C:\\Users\\user\\Pictures\\clickMe.png"));
    }

    public async void changeImage(int sleep, string uri)
    {
        await Task.Delay(sleep);
        BitmapSource source = new BitmapImage(new Uri(uri));
        source.Freeze();
        ImageSource = source;
    }

}

But whenever I assign MySpecialViewModel s Properties from MainViewModel , the RaisePropertyChange event does not force the Image element or other bindings to update from the MySpecialViewModel .但是每当我从MainViewModel分配MySpecialViewModel的属性时, RaisePropertyChange事件不会强制Image元素或其他绑定从MySpecialViewModel更新。

What am I doing wrong here?我在这里做错了什么? Is this a general wrong approach?这是一个普遍的错误方法吗?

You are too used to "View-First-Approach" (VFA).您太习惯于“查看优先方法”(VFA)。 Your situation is better off using "ViewModel-First-Approach" (VMFA).您的情况最好使用“ViewModel-First-Approach”(VMFA)。 In VFA, you place your child views from the main View, and each subview is linked to the respective ViewModel via DataContext .在 VFA 中,您将子视图放置在主视图中,每个子视图通过DataContext链接到相应的 ViewModel。

In VMFA, your ViewModel holds references of sub-ViewModels.在 VMFA 中,您的 ViewModel 包含子 ViewModel 的引用。 You expose these ViewModel references through property binding, and the View display them via DataTemplate .您通过属性绑定公开这些 ViewModel 引用,而 View 通过DataTemplate显示它们。

MainWindow.xaml主窗口.xaml

<Window x:Class="YouBug.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:YouBug"
    mc:Ignorable="d"
    DataContext="{Binding MainViewModel}"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.Resources>
        <DataTemplate DataType="{x:Type vm:MySpecialViewModel}">
            <local:MySpecialViewModel />
        </DataTemplate>
    </Grid.Resources>
    <ContentControl Content={Binding MySpecialView}" />
</Grid>

MainViewModel主视图模型

public class MainViewModel : ViewModelBase // Not sure why you didn't subclass ViewModelBase in your question
{
    private MySpecialViewModel _mySpecialViewModel;
    public MySpecialViewModel MySpecialViewModel
    {
        get
        {
            return _mySpecialViewModel;
        }
        set
        {
            if (value != _mySpecialViewModel)
            {
                _mySpecialViewModel = value;
                RaisePropertyChanged(); // The property changed method call
            }
        }
    }

    public MainViewModel()
    {
        MySpecialViewModel = new MySpecialViewModel();
        //gets not displayed!
        Task.Run(() => MySpecialViewModel.changeImage(5000, "C:\\Users\\user\\Pictures\\Capture.PNG"));
    }
}

MySpecialView does not need that DependencyProperty , nor set the DataContext . MySpecialView不需要那个DependencyProperty ,也不需要设置DataContext The DataContext is set automatically by the DataTemplate part. DataContextDataTemplate部分自动设置。 Your MySpecialViewModel can stay as it is now.您的MySpecialViewModel可以保持原样。

Edit编辑

I just realized your MainWindow is not doing the DataContext correctly either.我刚刚意识到您的 MainWindow 也没有正确执行DataContext

MainWindow.xaml.cs主窗口.xaml.cs

public partial class MainWindow: Window
{
    public MainWindow()
    {
        InitializeComponents();
        this.DataContext = new MainViewModel();
    }
}

Do not specify viewmodel property in you view, use DataContext.不要在视图中指定 viewmodel 属性,使用 DataContext。 See the following code.请参阅以下代码。

public partial class MySpecialView : UserControl
{

    public MySpecialView()
    {
        InitializeComponent();
    }
}

ViewModel for special:特殊视图模型:

public class MySpecialViewModel : ViewModelBase
{
    public BitmapSource imageSource { get; set; }
    public BitmapSource ImageSource { get { return imageSource; }
        set { if (value != imageSource)
            {
                imageSource = value;
 RaisePropertyChanged("ImageSource");
            }
        } }

    public MySpecialViewModel()
    {
        //gets displayed
        ImageSource = new BitmapImage(new Uri("C:\\Users\\user\\Pictures\\test.jpg"));

        //gets displayed aswell
        Task.Run(() => changeImage(10000, "C:\\Users\\user\\Pictures\\clickMe.png"));
    }

    public async void changeImage(int sleep, string uri)
    {
        await Task.Delay(sleep);
        BitmapSource source = new BitmapImage(new Uri(uri));
        source.Freeze();
        ImageSource = source;
    }

}

In XAML special:在 XAML 特殊:

    <UserControl x:Class="YouBug.MySpecialView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:YouBug"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Image Source="{Binding ImageSource}" />
    </Grid>

For main:对于主要:

public class MainViewModel : ViewModelBase
{
    public MySpecialViewModel SpecialViewModel
    { 
        get { return _specialViewModel; }
        set
        {
          if (value != _specialViewModel)
          {
             _specialViewModel= value;
             RaisePropertyChanged("SpecialViewModel");
          }
        } 
     }

    private MySpecialViewModel _specialViewModel;

        public MainViewModel()
        {
            MySpecialViewModel = new MySpecialViewModel();
            //gets not displayed!
            Task.Run(() => MySpecialViewModel.changeImage(5000, "C:\\Users\\user\\Pictures\\Capture.PNG"));
        }
    }

And in XAML:在 XAML 中:

<Window x:Class="YouBug.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:YouBug"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <MainWindowViewModel/
</Window.DataContext>
<Grid>
    <local:MySpecialView DataContext="{Binding Path=SpecialViewModel}"></local:MySpecialView>
</Grid>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM