我有一个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; }
EditableTextBlock
是UserControl
,双击时变为TextBox
,当失去焦点时变为TextBlock
。它还有一个名为IsInEditMode
的属性,默认情况下为false。如果是,则显示TextBox
。
问题:
ListBox的ItemsSouce
是ObservableCollection<Column>
。我有一个按钮,可以为集合添加新的Column
。但我的问题是,我想通过按钮为新添加的IsInEditMode
设置EditableTextBlock
。但我只能访问ViewModel中的Column
。如何访问EditableTextBlock
集合中指定Column
的{{1}}?
我能想出的唯一解决方案是从ItemsSource
派生一个类并为其添加一个属性(例如:name:Column
)(或者也许是一个包装类。Here '是一个类似的答案,建议使用包装类)和绑定到DataTemplate中的属性,如下所示:
IsInEditMode
但我不想要这个。我想要一些方法在XAML中执行此操作,而无需派生类和添加不必要的代码。 (并且还遵守MVVM规则)
答案 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
,这意味着现在将在编辑模式下启动创建的控件。
仅代码,仅查看的替代方法(类似于user2946329的答案)是挂钩True
处理程序,该处理程序将在添加新项目时触发。触发后,您现在正在处理新项目(通过布尔ListBox.ItemContainerGenerator.ItemsChanged
),该项目为新添加的项目找到适当DetectingNewItems
可视容器的第一个后代EditableTextBlock
控件。获得控件的引用后,请更改ListBoxItem
属性。
IsInEditMode
在视图中添加一个属性,该属性引用回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;
}