【问题标题】:WPF MVVM Access Element inside ItemsControl DataTemplateItemsControl DataTemplate 中的 WPF MVVM 访问元素
【发布时间】:2013-02-08 21:09:36
【问题描述】:

我需要从我的ComboBoxes 中获取值,这些值位于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 的内容。

这就是问题所在。我需要获得ComboBoxSelectedIndex(所以我将更改当前代码SelectedIndex="0")。如果我的ExternalClass 是包含那些SelectedIndex 值所需的,那将很容易,因为我已经可以访问它的字段(VariableValue)。但我需要那些int

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<int> SelectedIndices { get; set; }

    ...
}

或类似的东西。我制作了一个包含SelectedIndicesIVCollection 的快速包装类,但显然这行不通,因为ItemsControl 想要一个ObservableCollection,而不是一个有两个ObservableCollections 的类。

我猜这里问题的真正实质(我可能是错的,这就是我要问的原因)是我如何访问Style/DataTemplate 范围之外的属性我是目前在(例如IVCell)?我可以在代码隐藏中毫无问题地做到这一点,就像这样,

    private void IVValues_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        for (var i = 0; i < IndependentVariables.Items.Count; i++)
        {
            var uiElement = IndependentVariables.ItemContainerGenerator.ContainerFromIndex(i);
            var cBox = FindVisualChild<ComboBox>(uiElement); //Homebrew method
            //cBox.SelectedIndex
        }
    }

但我再次尝试保持对 MVVM 友好。我什至尝试过将事件命令与 MVVM Light 等一起使用,但这只会推迟问题。我需要解决它。或者绕过它。

【问题讨论】:

  • 因此,如果您设法将每个 ComboBox 的选定索引值绑定到您的 SelectedIndices 集合,您将如何跟踪哪个值来自哪个 ComboBox / ExternalClass?听起来 IVCollection 中项目的长度可能是固定的,因此您的意图是使用 SelectedIndices 中值的索引,但我并不完全清楚。
  • 您需要ExternalClass 中的另一个或两个属性来绑定 ComboBox 的 SelectedItem 和/或 SelectedIndex 以跟踪选择了哪些项目。

标签: wpf mvvm datatemplate itemscontrol


【解决方案1】:

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

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

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

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

希望对你有帮助

【讨论】:

  • IVCollection 是集合的集合。 IVCollection 中的每个项目都绑定到一个 ComboBox;换句话说,ComboBox 不绑定到IVCollection,至少如 OP 所述。
  • @MetroSmurf 你是对的。 IVCollection 中的每个项目都包含一个独立变量,其中每个 IV 都包含其名称和可能值的列表。根据这个答案,我需要一个数组或SelectedObjects 列表,可以为每个 ComboBox 的选定值解析。
【解决方案2】:

这是实现此目的的一种方法,尽管它并不比您的示例对 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 的方式。

【讨论】:

  • 感谢您的回答。至于纯数据绑定解决方案,我真的不能,因为我无法仅针对这种情况修改外部类。否则我早就这样做了!至于包装类解决方案,如果您参考我的问题,您会看到我尝试过。问题是ItemsControl 想要一个ObservableCollection,而不是包含IVCollectionSelectedObjects 的自定义类。另一方面,如果我将IVCollection 变成ObservableCollection&lt;CustomClass&gt;,现在我不知道如何访问ExternalClass 的变量和值。
【解决方案3】:

@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}"/>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多