C# 将ViewModel传递给UserControl

C# 将ViewModel传递给UserControl,c#,wpf,xaml,mvvm,user-controls,C#,Wpf,Xaml,Mvvm,User Controls,MySpecialView是一个复杂的图像控件,我想从不同的视图中重用它,并传递它的ViewModel,如本例所示 MainWindow.xaml <Window x:Class="YouBug.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmln

MySpecialView
是一个复杂的图像控件,我想从不同的视图中重用它,并传递它的
ViewModel
,如本例所示

MainWindow.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>
<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>
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>
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;
    }

}
但是每当我从
MainViewModel
分配
MySpecialViewModel
的属性时,
RaisePropertyChange
事件不会强制
Image
元素或其他绑定从
MySpecialViewModel
更新


我做错了什么?这是一种普遍错误的方法吗?

不要在视图中指定viewmodel属性,请使用DataContext。 请参阅下面的代码

public partial class MySpecialView : UserControl
{

    public MySpecialView()
    {
        InitializeComponent();
    }
}
特殊用途的ViewModel:

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;
    }

}
在XAML special中:

    <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>
在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>

您太习惯于“查看第一种方法”(VFA)。您最好使用“视图模型优先方法”(VMFA)。在VFA中,从主视图放置子视图,每个子视图通过
DataContext
链接到各自的视图模型

在VMFA中,ViewModel包含子ViewModels的引用。通过属性绑定公开这些ViewModel引用,视图通过
DataTemplate
显示它们

MainWindow.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>
<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>
MySpecialView
不需要该
dependencProperty
,也不需要设置
DataContext
DataContext
DataTemplate
部分自动设置。您的
MySpecialViewModel
可以保持现状

编辑 我刚刚意识到您的主窗口也没有正确执行
DataContext

MainWindow.xaml.cs

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

DataContext=ViewModel
什么。。。你在做什么?听着,你必须像对待其他控件一样对待你的用户控件。你认为文本框会做这样的事情吗?否。您的ViewModels/模型通过DataContext流到TextBox,并且您根据其DataContext中的内容绑定到TextBox上的属性。这一切都不是胡说八道。不要。请。@将
ViewModel
设置为
DataContext
没有问题。问题是有两个
BitmapSource
属性,而实际上应该只有一个。此外,从未创建视图模型的实例@Milleu请下次更彻底地研究你的问题,这条线索中的所有问题都已经在其他地方得到了回答。如果你对MVVM了解很少,你应该先从MVVM教程开始,试着学习相关的概念。我“没有感觉”的主要原因是让ViewModel在一些事件的代码中隐藏(不可避免)-ViewModel{get{return(MySpecialViewModel)DataContext;}更简单,但答案帮助很大,谢谢!)
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"));
    }
}
public partial class MainWindow: Window
{
    public MainWindow()
    {
        InitializeComponents();
        this.DataContext = new MainViewModel();
    }
}