If you have scope to add a new dependency property to the EditableTextBlock
user control you could consider adding one that has the name StartupInEditMode
, defaulting to false
to keep the existing behavior.
The Loaded
handler for the UserControl
could then determine the status of StartupInEditMode
to decide how to initially set the value of 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;
}
For controls already in the visual tree the changing value of StartupInEdit
does not matter as it is only evaluated once on creation. This means you can populate the collection of the ListBox
where each EditableTextBlock
is not in edit mode, then swap the StartupInEditmMode
mode to True
when you start adding new items. Then each new EditableTextBlock
control starts in the edit mode.
You could accomplish this switch in behavior by specifying a DataTemplate
where the Binding
of this new property points to a variable of the view and not the collection items.
<DataTemplate DataType="local:Column">
<utils:EditableTextBlock x:Name="editableTextBlock"
Text="{Binding Name, Mode=TwoWay}"
StartupInEditMode="{Binding ANewViewProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</DataTemplate>
You need to add a property to the parent Window
(or Page
or whatever is used as the containter for the view) called ANewViewProperty
in this example. This value could be part of your view model if you alter the binding to {Binding DataContext.ANewViewProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}
.
This new property (ANewViewProperty
) does not even need to implement INotifyPropertyChanged
as the binding will get the initial value as it is creating the new EditableTextBlock
control and if the value changes later it has no impact anyway.
You would set the value of ANewViewProperty
to False
as you load up the ListBox
ItemSource
initially. When you press the button to add a new item to the list set the value of ANewViewProperty
to True
meaning the control that will now be created starting up in edit mode.
Update: The C#-only, View-only alternative
The code-only, view-only alternative (similar to user2946329's answer)is to hook to the ListBox.ItemContainerGenerator.ItemsChanged
handler that will trigger when a new item is added. Once triggered and you are now acting on new items (via Boolean DetectingNewItems
) which finds the first descendant EditableTextBlock
control for the appropriate ListBoxItem
visual container for the item newly added. Once you have a reference for the control, alter the IsInEditMode
property.
//.... 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;
}
Update #2 (based on comments)
Add a property to the view that refers back to the the ViewModel assuming you keep a reference to the View Model in the DataContext
:-
..... // Add this to the Window/Page
public bool DetectingNewItems
{
get
{
var vm = DataContext as MyViewModel;
if (vm != null)
return vm.MyPropertyOnVM;
return false;
}
}
.....