MVVM 轻型用户控件未显示在主窗口中

本文关键字:窗口 显示 轻型 用户 控件 MVVM | 更新日期: 2023-09-27 18:31:47

我有点迷茫,不知道我缺少什么来获取用户控件视图(ImagePositionView)以加载和显示MainWindow;我一直在使用 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>

主视图模型.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);
        }
    }
}

视图模型定位器.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>

MVVM 轻型用户控件未显示在主窗口中

您的代码有几个问题,我将尝试一一枚举:

第一

在主窗口视图中,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);
        }
    }

并设置属性而不是 fild:

 //...
 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 命令,并使用 EventToCommandLoaded事件绑定到该命令,因此在 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 usercontrolloaded 事件绑定到您定义的命令:

    //..
    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"

MainWindow.xaml 中将它添加到MainWindowResources集合中:

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

请注意,您的命名空间可能不同,我的命名空间local.

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