【问题标题】:MVVM Light UserControl Not Displaying in MainWindowMVVM Light UserControl 未显示在主窗口中
【发布时间】:2016-01-25 21:55:17
【问题描述】:

对于通过 MainWindow 加载和显示 UserControl 视图 (ImagePositionView) 缺少什么,我有点迷茫;我一直在使用 MVVM Light 作为框架来促进这一点。目前所发生的只是 ViewModel 的命名空间路径显示在 MainWindow 中,而不是预期的图像。

这里是相关的文件,所以希望它是一些简单的东西让我逃脱了。

ImagePositionView.xaml:

<UserControl x:Class="PixelPosition.View.ImagePositionView"
             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:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:cmd="http://www.galasoft.ch/mvvmlight"
             xmlns:local="clr-namespace:PixelPosition"
             DataContext="{Binding ImagePosition, Source={StaticResource Locator}}"
             mc:Ignorable="d" 
             d:DesignHeight="600" d:DesignWidth="1000" Background="White">
    <Grid>

        <Viewbox HorizontalAlignment="Center">
            <Grid>
                <Image x:Name="ColourImage" Source="{Binding ColourImage}" Stretch="UniformToFill" />
            </Grid>
        </Viewbox>

    </Grid>

</UserControl>

MainViewModel.cs:

using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;

namespace PixelPosition.ViewModel
{

    public class MainViewModel : ViewModelBase
    {
        private string title = "This stupid thing isn't working :(";

        public string Title
        {
            get { return this.title; }
            set
            {
                if (this.title == value) return;
                this.title = value;
                RaisePropertyChanged("Title");
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
        }
    }
}

ImagePositionViewModel.cs:

using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GalaSoft.MvvmLight;

namespace PixelPosition.ViewModel
{
    public class ImagePositionViewModel : ViewModelBase
    {

        private WriteableBitmap colourBitmap = null;

        public ImageSource ColourImage
        {
            get
            {
                return this.colourBitmap;
            }
        }

        public ImagePositionViewModel()
        {
            // Open image to writeablebitmap
            string path = @"C:\Some\Path\To\ColorImage.png";

            Stream imageStreamSource = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
            var decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
            BitmapSource source = decoder.Frames[0];

            int width = source.PixelWidth;
            int height = source.PixelHeight;
            int stride = source.Format.BitsPerPixel / 8 * width;
            byte[] data = new byte[stride * height];
            source.CopyPixels(data, stride, 0);

            this.colourBitmap = new WriteableBitmap(width, height, 96.0, 96.0, source.Format, null);
            this.colourBitmap.WritePixels(new Int32Rect(0, 0, width, height), data, stride, 0);

        }

    }
}

ViewModelLocator.cs:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;

namespace PixelPosition.ViewModel
{

    public class ViewModelLocator
    {

        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<ImagePositionViewModel>();
        }

        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        public ImagePositionViewModel ImagePosition
        {
            get
            {
                return ServiceLocator.Current.GetInstance<ImagePositionViewModel>();
            }
        }

        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
}   

App.xaml:

<Application x:Class="PixelPosition.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:PixelPosition"
             StartupUri="MainWindow.xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:vm="clr-namespace:PixelPosition.ViewModel"
             d1p1:Ignorable="d"
             xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
  <Application.Resources>
    <ResourceDictionary>
      <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
    </ResourceDictionary>
  </Application.Resources>
</Application>

MainWindow.xaml:

<Window x:Class="PixelPosition.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:PixelPosition"
        DataContext="{Binding Main, Source={StaticResource Locator}}"
        mc:Ignorable="d"
        Title="{Binding Title}" Height="800" Width="1000">
    <Grid Margin="10 0 10 0">
        <Border Background="GhostWhite" BorderBrush="LightGray" BorderThickness="1" CornerRadius="5">
            <ContentControl Content="{Binding ImagePosition, Source={StaticResource Locator}}" />
        </Border>

    </Grid>
</Window>

【问题讨论】:

  • ImagePositionViewModel 中的构造函数是 ManualSelectionViewModel 吗?是不是打错字了?
  • @SamTheDev 是的,这是一个错字,试图为我的问题代码提供一个更简单/更通用的示例。那个现在已经修好了,所以希望没有任何其他错别字。

标签: c# wpf xaml mvvm-light


【解决方案1】:

你的代码有几个问题,我会尝试一一列举:

第一

在主窗口视图中,ContentControl 的内容绑定到 ImagePositionViewModel 实例是错误的,内容需要绑定到 UserControl 的实例:

 <Border Background="GhostWhite" BorderBrush="LightGray" BorderThickness="1" CornerRadius="5">
        <ContentControl  >
            <YourNameSpace:ImagePositionView/>
        </ContentControl>
    </Border>

您可以考虑将 ContentControl 的 Content 属性绑定到 MainViewModel 中定义的属性,该属性将包含对您要显示的 UserControl 的引用。

第二

ImagePositionViewModel 中,您需要正确定义ColourImage 属性并将其设置为bitmapImage,而不是设置字段colourBitmap,这样会通知UI,因为RaisePropertyChanged 将被调用:

public const string ColourImagePropertyName = "ColourImage";
    private WriteableBitmap  colourBitmap =  null;
    public WriteableBitmap  ColourImage
    {
        get
        {
            return colourBitmap ;
        }

        set
        {
            if (Equals(colourBitmap, value))
            {
                return;
            }

            colourBitmap  = value;
            RaisePropertyChanged(ColourImagePropertyName);
        }
    }

并设置属性而不是字段:

 //...
 int stride = source.Format.BitsPerPixel / 8 * width;
 byte[] data = new byte[stride * height];
 source.CopyPixels(data, stride, 0);
 var cb = new WriteableBitmap(width, height, 96.0, 96.0, source.Format, null);
 cb.WritePixels(new Int32Rect(0, 0, width, height), data, stride, 0);
 ColourImage=cb;

终于

您的代码现在应该可以工作了,但是,在 VM 的构造函数中加载图像仍然是个坏主意,您应该在 UserControl 的 ViewModel 中定义一个 Loaded 命令并将 Loaded 事件绑定到它命令使用EventToCommand,所以在ImagePosition VM 中定义一个LoadedCommand 像这样:

 private RelayCommand _loadedCommand;
    public RelayCommand LoadedCommand 
    {
        get
        {
            return _loadedCommand
                ?? (_loadedCommand = new RelayCommand(
                () =>
                {
                    // Open image to writeablebitmap
                    string path = @"C:\Some\Path\To\ColorImage.png";

                    Stream imageStreamSource = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
                    var decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                    BitmapSource source = decoder.Frames[0];

                    int width = source.PixelWidth;
                    int height = source.PixelHeight;
                    int stride = source.Format.BitsPerPixel / 8 * width;
                    byte[] data = new byte[stride * height];
                    source.CopyPixels(data, stride, 0);

                    var cb = new WriteableBitmap(width, height, 96.0, 96.0, source.Format, null);
                    cb.WritePixels(new Int32Rect(0, 0, width, height), data, stride, 0);
                    ColourImage = cb;
                }));
        }
    }

*并从Vm构造器中移除图像加载代码,

然后在ImagePositionView usercontrol 中将loaded 事件绑定到您定义的命令:

    //..
    d:DesignHeight="600" d:DesignWidth="1000" Background="White" >

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <command:EventToCommand Command="{Binding Mode=OneWay, Path=LoadedCommand}"
                        PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>

    <Grid>
 //..

您应该添加以下命名空间:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"

【讨论】:

  • mvvmLight6 是对应该位于 MainWindow.xaml 顶部的特定程序集的引用吗?
  • 不,我的命名空间,改用你的
  • 酷,开始了,现在来看看你的其他建议。
  • 这可能超出了注释的范围,但是当将 ContentControl 的 Content 属性绑定到 MainViewModel 中定义的属性以保存对我要显示的 UserControl 的引用时。这是 ViewModelLocator 的用途之外,还是我以特定方式使用它?
  • ViewModelLocator 只是视图和 ViewModel 之间的桥梁,不要将它用于其他用途,除此之外请记住,在渲染视图之前不会创建 Vm 实例
【解决方案2】:

将此添加到您的 MainWindowResources MainWindow.xaml 集合中:

<Window.Resources>
    <DataTemplate DataType="{x:Type local:ImagePositionViewModel}">
        <local:ImagePositionView />
    </DataTemplate>
</Window.Resources>

注意,你的命名空间可能不同,我的是local

ImagePositionViewModel 已正确加载到ContentControl,问题是它只是不知道如何实际“渲染”它,所以我们为它提供了一个DataTemplate

【讨论】:

  • 如果视图/视图模型发生变化,Window.Resources 会发生变化吗?好像我有两个视图并在它们之间切换?
猜你喜欢
  • 1970-01-01
  • 2016-11-10
  • 1970-01-01
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 2020-09-04
  • 1970-01-01
  • 2017-06-17
相关资源
最近更新 更多