从ListBox中的数据对象获取DataTemplate

时间:2016-01-04 13:13:48

标签: c# wpf mvvm listbox datatemplate

我有一个ListBox ItemTemplate看起来像这样:

<DataTemplate DataType="local:Column">
    <utils:EditableTextBlock x:Name="editableTextBlock" Text="{Binding Name, Mode=TwoWay}"/>
</DataTemplate>

Column是一个简单的类,如下所示:

public Column(string name, bool isVisibleInTable)
{
    Name = name;
    IsVisibleInTable = isVisibleInTable;
}

public string Name { get; set; }
public bool IsVisibleInTable { get; set; }

EditableTextBlockUserControl,双击时变为TextBox,当失去焦点时变为TextBlock。它还有一个名为IsInEditMode的属性,默认情况下为false。如果是,则显示TextBox

问题:
ListBox的ItemsSouceObservableCollection<Column>。我有一个按钮,可以为集合添加新的Column。但我的问题是,我想通过按钮为新添加的IsInEditMode设置EditableTextBlock。但我只能访问ViewModel中的Column。如何访问EditableTextBlock集合中指定Column的{​​{1}}?

我能想出的唯一解决方案是从ItemsSource派生一个类并为其添加一个属性(例如:name:Column)(或者也许是一个包装类。Here '是一个类似的答案,建议使用包装类)和绑定到DataTemplate中的属性,如下所示:

IsInEditMode

但我不想要这个。我想要一些方法在XAML中执行此操作,而无需派生类和添加不必要的代码。 (并且还遵守MVVM规则)

2 个答案:

答案 0 :(得分:2)

如果您有可以向EditableTextBlock用户控件添加新依赖项属性的范围,则可以考虑添加名称为StartupInEditMode的默认值,默认为false以保留现有行为。

Loaded的{​​{1}}处理程序可以确定UserControl的状态,以决定如何初始设置StartupInEditMode的值。

IsInEditMode

对于已在可视树中的控件,//..... Added to EditableTextBlock user control public bool StartupInEdit { get { return (bool)GetValue(StartupInEditProperty); } set { SetValue(StartupInEditProperty, value); } } public static readonly DependencyProperty StartupInEditProperty = DependencyProperty.Register("StartupInEdit", typeof(bool), typeof(EditableTextBlock ), new PropertyMetadata(false)); private void EditableTextBlock_OnLoaded(object sender, RoutedEventArgs e) { IsInEditMode = StartupInEditMode; } 的更改值无关紧要,因为它仅在创建时评估一次。这意味着您可以填充每个StartupInEdit未处于编辑模式的ListBox集合,然后在开始添加新项目时将EditableTextBlock模式更改为StartupInEditmMode。然后每个新的True控件都以编辑模式启动。

您可以通过指定EditableTextBlock来完成此行为切换,其中此新属性的DataTemplate指向视图的变量而不是集合项。

Binding

在此示例中,您需要向父 <DataTemplate DataType="local:Column"> <utils:EditableTextBlock x:Name="editableTextBlock" Text="{Binding Name, Mode=TwoWay}" StartupInEditMode="{Binding ANewViewProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/> </DataTemplate> (或Window或用作视图的内容的任何内容)添加一个名为Page的属性。如果您将绑定更改为ANewViewProperty,则此值可能是视图模型的一部分。

这个新属性({Binding DataContext.ANewViewProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}})甚至不需要实现ANewViewProperty,因为绑定将在创建新INotifyPropertyChanged控件时获取初始值,如果值稍后更改无论如何它都没有影响。

在您最初加载EditableTextBlock ANewViewProperty时,您会将False的值设置为ListBox。当您按下按钮将新项目添加到列表时,将ItemSource的值设置为ANewViewProperty,这意味着现在将在编辑模式下启动创建的控件。

更新:C#-only,仅查看选项

仅代码,仅查看的替代方法(类似于user2946329的答案)是挂钩True处理程序,该处理程序将在添加新项目时触发。触发后,您现在正在处理新项目(通过布尔ListBox.ItemContainerGenerator.ItemsChanged),该项目为新添加的项目找到适当DetectingNewItems可视容器的第一个后代EditableTextBlock控件。获得控件的引用后,请更改ListBoxItem属性。

IsInEditMode

更新#2(基于评论)

在视图中添加一个属性,该属性引用回ViewModel,假设您在//.... View/Window Class private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { MyListBox.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged; } private void ItemContainerGenerator_ItemsChanged(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e) { if ((e.Action == NotifyCollectionChangedAction.Add) && DetectingNewItems) { var listboxitem = LB.ItemContainerGenerator.ContainerFromIndex(e.Position.Index + 1) as ListBoxItem; var editControl = FindFirstDescendantChildOf<EditableTextBlock>(listboxitem); if (editcontrol != null) editcontrol.IsInEditMode = true; } } public static T FindFirstDescendantChildOf<T>(DependencyObject dpObj) where T : DependencyObject { if (dpObj == null) return null; for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dpObj); i++) { var child = VisualTreeHelper.GetChild(dpObj, i); if (child is T) return (T)child; var obj = FindFirstChildOf<T>(child); if (obj != null) return obj; } return null; } 中保留对视图模型的引用: -

DataContext

答案 1 :(得分:1)

要在模板中获取元素并在代码中更改它所需的属性FrameworkTemplate.FindName Method (String, FrameworkElement)

private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is childItem)
            return (childItem)child;
        else
        {
            childItem childOfChild = FindVisualChild<childItem>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}

然后:

for (int i = 0; i < yourListBox.Items.Count; i++)
{
    ListBoxItem yourListBoxItem = (ListBoxItem)(yourListBox.ItemContainerGenerator.ContainerFromIndex(i));
    ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(yourListBoxItem);
    DataTemplate myDataTemplate = contentPresenter.ContentTemplate;
    EditableTextBlock editable = (EditableTextBlock) myDataTemplate.FindName("editableTextBlock", contentPresenter);
    //Do stuff with EditableTextBlock
    editable.IsInEditMode = true;
}