ItemsControl DataTemplate中的WPF MVVM访问元素

时间:2013-02-08 21:09:37

标签: wpf mvvm datatemplate itemscontrol

我需要通过ComboBox获取位于ObservableCollection内的ItemsControl内的值。我需要将它们存储在一个单独的数据结构中,至少我认为我做了。听起来很简单?我希望是这样,我一直坚持下去。

这是我的XAML:

<Window.Resources>
    <Style x:Key="IVCell" TargetType="{x:Type ItemsControl}">
        <Setter Property="ItemTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Variable.Name}"/>
                        <ComboBox x:Name="IVValues" ItemsSource="{Binding Values}"
                                  SelectedIndex="0"/>
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

...
        <ItemsControl Name="IndependentVariables" Style="{StaticResource IVCell}" ItemsSource="{Binding IVCollection}"/>

这里定义了IVCollection,在我的ViewModel中:

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<ExternalClass> IVCollection { get; set; }

    ...
}

在您询问ExternalClass之前,请知道包含VariableValues的内容。

这是问题所在。我需要获取SelectedIndex的{​​{1}}(所以我会更改当前代码ComboBox)。如果我的SelectedIndex="0"是需要包含这些ExternalClass值的内容,那就很容易了,因为我已经可以访问其字段(SelectedIndexVariable)。但我需要

中的Value个值
int

或类似的东西。我创建了一个包含public class MainViewModel : ViewModelBase { public ObservableCollection<int> SelectedIndices { get; set; } ... } SelectedIndices的快速包装类,但显然这不起作用,因为IVCollection想要一个ItemsControl,而不是一个有两个{ {1}}秒。

我想这里问题的真正含义(我可能错了,这就是我要问的原因)是如何访问超出ObservableCollection / {{1}范围的属性我现在在(例如ObservableCollection)?我可以在代码隐藏中完成它,没有问题,就像这样,

Style

但是我再一次尝试保持MVVM友好。我甚至尝试过使用MVVM Light等事件命令,但这只是推迟了问题。我需要解决它。或绕过它。

3 个答案:

答案 0 :(得分:2)

我会这样做,我会为所选值创建一个属性

// within the MainViewModel class
private ExternalClass _selectedObject;
public ExternalClass SelectedObject
 {
   get { return _selectedObject; }
   set
       {
            _selectedObject= value;
            RaisePropertyChanged("SelectedObject");
       }
  }

然后将SelectedItem中的ComboBox绑定到SelectedObject属性

<ComboBox ItemsSource="{Binding IVCollection}" SelectedItem="{Binding SelectedObject}" >
        <ComboBox.ItemTemplate>
            <DataTemplate>
                ... data template goes here
            </DataTemplate>
        </ComboBox.ItemTemplate>
 </ComboBox>

希望这会有所帮助

答案 1 :(得分:1)

这是可以实现的一种方式,虽然它不比MVVM友好得多。

在MainWindow中,在显示视图之前的某处添加以下路由事件处理程序,如构造函数或加载的事件:

AddHandler(Selector.SelectionChangedEvent, new SelectionChangedEventHandler(OnSelectionChanged));

然后添加此处理程序:

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
     var comboBox = e.OriginalSource as ComboBox;
     if (comboBox == null) return;
     var context = comboBox.DataContext as ExternalClass;
     if (context == null) return;
     var indexOfContext = IVCollection.IndexOf(context);
     if (indexOfContext < 0) return;
     SelectedIndices[indexOfContext] = comboBox.SelectedIndex;
}

我仍然不确定你打算如何跟踪哪个SelectedIndex与哪些数据一致,所以在上面的例子中我假设SelectedIndices有一个固定的长度。

如果您正在寻找纯数据绑定解决方案,我强烈建议您只在ExternalClass上放置一个属性并绑定它。或者,如果您反对这一点,请在ExternalClass周围使用一个包装类,它将ExternalClass作为属性公开,将SelectedIndex公开为另一个。这将是最简单,最MVVM友好的方式。

答案 2 :(得分:0)

@sleiman和@Brandon 你的答案都不是100%,但它们仍然非常有用。我使用你的两个答案中的元素达到了我的解决方案,所以我不愿意接受另一个。

我使用了包装类解决方案和公开属性解决方案的组合(以及半无关但仍然必需的事件处理技术)来创建以下内容:

public class IVWrapper : INotifyPropertyChanged
{
    public VariableValueList<double> IVCollection { get; set; }
    private int selectedIndex;
    public int SelectedIndex
    {
        get { return selectedIndex; }
        set
        {
            selectedIndex = value;
            OnPropertyChanged("SelectedIndex");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;

        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MainViewModel : ViewModelBase
{

    public ObservableCollection<IVWrapper> IVWrapperCollection { get; set; }

    ...

    private void InitializeIVs()
    {
        IVWrapperCollection.Clear();
        foreach (var iv in ...)
        {
            var toBeAdded = new IVWrapper {IVCollection = iv};
            toBeAdded.PropertyChanged += (sender, args) => Foo();
            IVWrapperCollection.Add(toBeAdded);
        }
    }

    public void Foo()
    {
        //Doin stuffs
    }
}

与我的ViewModel的这种额外集成允许我在selectionIndex更改时调用函数。整齐!干净!

XAML MainView中的相应更改:

                    <StackPanel>
                        <TextBlock Text="{Binding IVCollection.Variable.Name}"/>
                        <ComboBox ItemsSource="{Binding IVCollection.Values}"
                                  SelectedIndex="{Binding SelectedIndex}"/>
                    </StackPanel>

...

        <ItemsControl Name="IndependentVariables" Style="{StaticResource IVCell}" ItemsSource="{Binding IVWrapperCollection}"/>