[英]Get DataTemplate from data object in ListBox
我有一個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
,當Lost Focus時變回TextBlock
。 它還有一個名為IsInEditMode
的屬性,默認情況下為false。 如果為true,則顯示TextBox
。
問題:
ListBox的ItemsSouce
是一個ObservableCollection<Column>
。 我有一個按鈕,可以為集合添加新的Column
。 但我的問題是我希望IsInEditMode
被該Button重新添加為EditableTextBlock
。 但我只能在ViewModel中訪問Column
。 如何訪問ItemsSource
集合中指定Column
的EditableTextBlock
?
我能想出的唯一解決方案是從Column
派生一個類並為其添加一個屬性(例如:name: IsInEditMode
)(或者可能是一個包裝類。 這里有一個類似的答案,建議使用包裝類)並綁定到該類DataTemplate中的屬性如下:
<DataTemplate DataType="local:DerivedColumn">
<utils:EditableTextBlock x:Name="editableTextBlock" Text="{Binding Name, Mode=TwoWay}"
IsInEditMode="{Binding IsInEditMode}"/>
</DataTemplate>
但我不想要這個。 我想要一些方法在XAML中執行此操作,而無需派生類和添加不必要的代碼。 (並且還遵守MVVM規則)
如果您有可以向EditableTextBlock
用戶控件添加新依賴項屬性的范圍,則可以考慮添加名為StartupInEditMode
默認值,默認為false
以保留現有行為。
然后, UserControl
的Loaded
處理程序可以確定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
控件,如果值稍后更改它無論如何都沒有影響。
在最初加載ListBox
ItemSource
您可以將ANewViewProperty
的值設置為False
。 當您按下按鈕將新項目添加到列表時,將ANewViewProperty
的值設置為True
意味着現在將在編輯模式下啟動創建的控件。
僅代碼,僅查看的替代方法(類似於user2946329的答案)是掛鈎ListBox.ItemContainerGenerator.ItemsChanged
處理程序,該處理程序將在添加新項目時觸發。 觸發后,您現在正在處理新項目(通過布爾DetectingNewItems
新DetectingNewItems
),該新項目為新添加的項目找到適當的ListBoxItem
可視容器的第一個后代EditableTextBlock
控件。 獲得控件的引用后,請更改IsInEditMode
屬性。
//.... 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
保留對View Model的引用,請向視圖添加一個引用ViewModel的屬性: -
..... // Add this to the Window/Page
public bool DetectingNewItems
{
get
{
var vm = DataContext as MyViewModel;
if (vm != null)
return vm.MyPropertyOnVM;
return false;
}
}
.....
要在模板中獲取元素並在代碼中更改它的屬性,您需要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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.