(MVVM / WPF)在View-Model中操作视图元素

时间:2016-11-12 14:54:28

标签: c# wpf listview mvvm

我有一个 ListView ,上面有一些图像,我希望这样工作: enter image description here

我使用命令 CommandParameter ListView 发送到 View-Model ,然后操作<每张图片的em> width 和 height

<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
        <i:InvokeCommandAction Command="{Binding MVM.SelectedChangedCommand}" CommandParameter="{Binding ElementName=listView}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>  

视图模型:

private void SelectedChangedAction(object param)    
{
    ListView s = (ListView)param;

    double j = 1;
    for (int i = s.SelectedIndex - 1; i >= 0 && j >= 0; i--, j -= 0.15)
    {
        Pic t = (Pic)s.Items[i];
        t.Width = 150 * j;
        t.Height = 250 * j;
    }


    j = 1;
    for (int i = s.SelectedIndex + 1; i < s.Items.Count && j >= 0; i++, j -= 0.15)
    {
        Pic t = (Pic)s.Items[i];
        t.Width = 150 * j;
        t.Height = 250 * j;
    }
    s.ScrollIntoView(s.Items[s.SelectedIndex]);
}

但正如我所说,您可以在 View-Model 中看到,我在 VM 中使用了 ListView ,我认为这是违规行为!那么我怎么能在 MVVM 中做这样的事情呢? (当然我认为我可以构建一个基于ListView的新控制器,可以做到这一点,虽然我需要像XAML这样的简单解决方案)

3 个答案:

答案 0 :(得分:1)

在ViewModel中为SelectedItem创建一个属性。将其绑定在View的ListView中。使用转换器设置绑定高度和宽度,并将所有逻辑移到那里。

答案 1 :(得分:1)

虽然您已经接受了解决方案,但我发现这个问题很有趣,所以我找到了以下解决方案:

<强> XAML:

<Window x:Class="SO40564064.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:SO40564064"        
        mc:Ignorable="d"
        Title="MainWindow" Height="500" Width="600">
    <Grid>
    <ListView Name="TheView" ItemsSource="{Binding Images}">
      <ListView.ItemTemplate>
        <DataTemplate>
          <local:MyImageBorder Width="{Binding Width}" 
                  Height="{Binding Height}"
                  Background="{Binding Color}"
                  IsMouseOverMe="{Binding MouseIsOverMe, Mode=TwoWay}" />
        </DataTemplate>
      </ListView.ItemTemplate>
      <ListView.ItemsPanel>
        <ItemsPanelTemplate>
          <StackPanel Orientation="Horizontal"></StackPanel>
        </ItemsPanelTemplate>
      </ListView.ItemsPanel>
    </ListView>
    </Grid>
</Window>

代码背后:

namespace SO40564064
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      DataContext = new ViewModel();
    }    
  }
}

<强>视图模型:

  public class ViewModel : INotifyPropertyChanged
  {
    public ViewModel()
    {
      m_images = new ObservableCollection<MyImage>(
        new MyImage[] {
          new MyImage() { Color = Brushes.Red, Height = 100, Width = 70 },
          new MyImage() { Color = Brushes.Green, Height = 100, Width = 70 },
          new MyImage() { Color = Brushes.Blue, Height = 100, Width = 70 },
          new MyImage() { Color = Brushes.Yellow, Height = 100, Width = 70 },
          new MyImage() { Color = Brushes.Magenta, Height = 100, Width = 70 },
          new MyImage() { Color = Brushes.Cyan, Height = 100, Width = 70 }
          });

      foreach (var image in m_images)
      {
        image.PropertyChanged += Image_PropertyChanged;
      }
    }

    private void Image_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      switch (e.PropertyName)
      {
        case "MouseIsOverMe":

          var curImage = sender as MyImage;
          var curIndex = m_images.IndexOf(curImage);

          if (curImage.MouseIsOverMe)
          {
            if (curIndex > 0)
            {
              m_images[curIndex - 1].Width *= 1.2;
              m_images[curIndex - 1].Height *= 1.2;
            }
            if (curIndex < m_images.Count - 2)
            {
              m_images[curIndex + 1].Width *= 1.2;
              m_images[curIndex + 1].Height *= 1.2;
            }

            m_images[curIndex].Width *= 1.5;
            m_images[curIndex].Height *= 1.5;
          }
          else
          {
            if (curIndex > 0)
            {
              m_images[curIndex - 1].Width /= 1.2;
              m_images[curIndex - 1].Height /= 1.2;
            }
            if (curIndex < m_images.Count - 2)
            {
              m_images[curIndex + 1].Width /= 1.2;
              m_images[curIndex + 1].Height /= 1.2;
            }

            m_images[curIndex].Width /= 1.5;
            m_images[curIndex].Height /= 1.5;
          }
          break;
        default:
          break;
      }
    }

    private ObservableCollection<MyImage> m_images;
    public ObservableCollection<MyImage> Images
    {
      get { return m_images; }
      set
      {
        m_images = value;
        OnPropertyChanged("Images");
      }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string property)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
  }

图片ViewModel类:

  public class MyImage : INotifyPropertyChanged
  {
    private double m_width;
    public double Width
    {
      get { return m_width; }
      set
      {
        m_width = value;
        OnPropertyChanged("Width");
      }
    }

    private double m_height;
    public double Height
    {
      get { return m_height; }
      set
      {
        m_height = value;
        OnPropertyChanged("Height");
      }
    }

    private Brush m_color;
    public Brush Color
    {
      get { return m_color; }
      set
      {
        m_color = value;
        OnPropertyChanged("Color");
      }
    }

    private bool m_mouseIsOverMe;
    public bool MouseIsOverMe
    {
      get { return m_mouseIsOverMe; }
      set
      {
        m_mouseIsOverMe = value;
        OnPropertyChanged("MouseIsOverMe");
      }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string property)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
  }

<强>&#34;图像&#34; Control Subclass(这里是Border子类):

using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace SO40564064
{
  public class MyImageBorder : Border
  {
    public MyImageBorder()
    {
      IsMouseDirectlyOverChanged += MyImageBorder_IsMouseDirectlyOverChanged;
    }

    public bool IsMouseOverMe
    {
      get { return (bool)GetValue(IsMouseOverMeProperty); }
      set { SetValue(IsMouseOverMeProperty, value); }
    }

    public static readonly DependencyProperty IsMouseOverMeProperty =
        DependencyProperty.Register("IsMouseOverMe", typeof(bool), typeof(MyImageBorder), new PropertyMetadata(false));

    private void MyImageBorder_IsMouseDirectlyOverChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
    {
      IsMouseOverMe = (bool)e.NewValue;
    }
  }
}

为了我自己的方便,我已经将一个边框子类化,你可以将一个图像子类化(或者你可以包装图像的东西)。

当鼠标悬停在每个图像上时,解决方案仍然有效 - 而不是在选择图像时。您可以使用自定义样式将悬停元素的丑陋默认边框设置为样式。

答案 2 :(得分:0)

您真的不应该直接操作视图模型中的视图组件。相反,应该通过编写自定义布局面板来实现此机制,该面板执行视图中元素的大小调整。 This page有一些关于如何实现这一目标的好信息。