WPF style depending on checkbox state - wpf

I am creating a settings editor where plugin writers can define their own user interface for configuring their plugins. I am implementing a feature to hide certain "advanced" elements if a checkbox is unchecked.
The checkbox XAML is trivial:
<CheckBox Name="isAdvanced">_Advanced</CheckBox>
Ideally (more on this later), implementors would just add a flag to advanced controls (which should be hidden when the "advanced" checkbox is unchecked) like so:
<Button library:MyLibraryControl.IsAdvanced="True">My Button</Button>
The problem lies in making the magic of hiding the IsAdvanced="True" elements when isAdvanced.IsChecked == false. I have the desired behaviour with this style on the window element:
<Window.Resources>
<Style TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding (library:MyLibraryControl.IsAdvanced), RelativeSource={RelativeSource Mode=Self}}" Value="True" />
<Condition Binding="{Binding IsChecked, ElementName=isAdvanced}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="UIElement.Visibility" Value="Collapsed" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
However, this method presents two problems:
It only adds functionality to buttons and nothing else. The IsAdvanced flag can (should be able to) be added to any visual element.
It replaces/overrides the styles which would otherwise be on the button.
Is there some other way to produce the functionality I want? I'm not afraid of working in the code-behind, but an elegant XAML solution is ideal (as this is purely a UI change, aside from saving the state of the checkbox in the user's preferences).
Some other methods of signifying advanced elements have come to mind. These include using a dynamic resource and directly binding:
<Button Visibility="{DynamicResource IsAdvancedVisibility}">My Button</Button>
<Button Visibility="{Binding IsChecked, RelativeSource={...}, ValueConverter={...}}">My Button</Button>
Using a resource dictionary would probably work, but it seems like a really bad solution as UI state doesn't seem like it should belong in a dictionary. Binding manually is quite the mess because the state of the checkbox has to be sent somehow to the element, and aside from hardcoding values I don't see it not becoming a mess.
Both of these alternate solutions tie semantics ("this is an advanced option") to appearance ("advanced options should be collapsed"). Coming from the HTML world, I know this is a very bad thing, and I refuse to submit to these methods unless absolutely necessary.

How about moving this into the ViewModel instead of XAML because this looks like behavior to me.
The behavior you want seems to me - each plugin registers a bunch of properties (mapping to UI Controls) as advanced. There is a global setting to turn on/off advanced properties. When this happens, update all plugins to show/hide their advanced properties
Have plugin writers implement an interface containing a set only property AreAdvancedControlsVisible. Let them take care of hiding/showing the controls in their UI via property change handler. The advanced UI controls can bind to a ShowAdvancedControls flag on the pluginVM, which is toggled on/off from the prop changed handler.
The framework can just loop over the available plugins and set this flag whenever the ShowAdvanced checkbox is set.

There are probably alot of better ways to solve this problem but I tried to work past the two issues you had with your solution. Small sample project with this can be downloaded here.
1.It only adds functionality to buttons and nothing else. The
IsAdvanced flag can (should be able
to) be added to any visual element.
Adding an Attached Property, that make all children inherit the value, to the top-most container could fix this.
2.It replaces/overrides the styles which would otherwise be on the
button.
Bea Stollnitz has a nice blog article about merging Styles here.
It has an extension method for Style called Merge which could be used.
Sounded pretty straight forward but the following problems made the code more complex.
1. The Visual elements doesn't have a style when the Attached Property is inherited. Required Loaded event.
2. A Style can't be modified when it is in use. Required a copy method for the Style.
So, we want this Style to be merged with the active Style for all children in the parent container.
<Style x:Key="IsAdvancedStyle">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding (library:MyLibraryControl.IsAdvanced), RelativeSource={RelativeSource Mode=Self}}" Value="True" />
<Condition Binding="{Binding IsChecked, ElementName=isAdvanced}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Control.Visibility" Value="Collapsed" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
If the root container is a StackPanel we then add this. The style IsAdvancedStyle will then be inherited by all the children and merged with the active Style.
<StackPanel local:StyleChildsBehavior.StyleChilds="{StaticResource IsAdvancedStyle}">
StyleChildsBehavior.cs
public class StyleChildsBehavior
{
public static readonly DependencyProperty StyleChildsProperty =
DependencyProperty.RegisterAttached("StyleChilds",
typeof(Style),
typeof(StyleChildsBehavior),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits,
StyleChildsCallback));
public static void SetStyleChilds(DependencyObject element, Style value)
{
element.SetValue(StyleChildsProperty, value);
}
public static Style GetStyleChilds(DependencyObject element)
{
return (Style)element.GetValue(StyleChildsProperty);
}
private static void StyleChildsCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(d) == true)
{
return;
}
Style isAdvancedStyle = e.NewValue as Style;
if (isAdvancedStyle != null)
{
FrameworkElement element = d as FrameworkElement;
if (element != null)
{
if (element.IsLoaded == false)
{
RoutedEventHandler loadedEventHandler = null;
loadedEventHandler = new RoutedEventHandler(delegate
{
element.Loaded -= loadedEventHandler;
MergeStyles(element, isAdvancedStyle);
});
element.Loaded += loadedEventHandler;
}
else
{
MergeStyles(element, isAdvancedStyle);
}
}
}
}
private static void MergeStyles(FrameworkElement element, Style isAdvancedStyle)
{
if (element != null)
{
Style advancedStyle = GetStyleCopy(isAdvancedStyle);
advancedStyle.Merge(element.Style);
element.Style = advancedStyle;
}
}
private static Style GetStyleCopy(Style style)
{
string savedStyle = XamlWriter.Save(style);
using (MemoryStream memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(savedStyle)))
{
ParserContext parserContext = new ParserContext();
parserContext.XmlnsDictionary.Add("library", "clr-namespace:HideAll;assembly=HideAll");
return XamlReader.Load(memoryStream, parserContext) as Style;
}
}
}
After this the IsAdvancedStyle will be merged in all children of the StackPanel and this goes for children that are added in run-time as well.
Modified Merge extension method from the blog link.
public static void Merge(this Style style1, Style style2)
{
if (style1 == null || style2 == null)
{
return;
}
if (style1.TargetType.IsAssignableFrom(style2.TargetType))
{
style1.TargetType = style2.TargetType;
}
if (style2.BasedOn != null)
{
Merge(style1, style2.BasedOn);
}
foreach (SetterBase currentSetter in style2.Setters)
{
style1.Setters.Add(currentSetter);
}
foreach (TriggerBase currentTrigger in style2.Triggers)
{
style1.Triggers.Add(currentTrigger);
}
}

I decided to invert the problem a little bit, and it worked well.
Instead of dealing with styles, I used property binding as suggested by Gishu. However, instead of placing the UI in the VM (where properties would propagate several layers manually), I used an attached property named ShowAdvanced which propagates down via property inheritance.
Creating this property is trivial:
public static readonly DependencyProperty ShowAdvancedProperty;
ShowAdvancedProperty = DependencyProperty.RegisterAttached(
"ShowAdvanced",
typeof(bool),
typeof(MyLibraryControl),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior
)
);
The checkbox sets the ShowAdvanced property above on the entire window. It could set it elsewhere (e.g. on the grid), but putting it on the window makes more sense IMO:
<CheckBox Grid.Column="0"
IsChecked="{Binding (library:MyLibraryControl.ShowAdvanced), ElementName=settingsWindow}"
Content="_Advanced" />
Changing the visibility (or whatever other properties desired) depending on the ShowAdvanced property becomes easy:
<Foo.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Foo.Resources>
<Button Visibility="{Binding (library:MyLibraryControl.ShowAdvanced), RelativeSource={RelativeSource Self}, Converter={StaticResource BooleanToVisibilityConverter}}">I'm Advanced</Button>
Ditching styles allows plugin writers to completely change the layout of their controls if they need to. They can also show advanced controls but keep them disabled if desired. Styles brought up a lot of problems and, as Meleak showed, the workarounds were messy.
My main problem with putting the 'advanced' display logic in the VM is that it is now less likely you can get away with binding multiple views to the same VM while maintaining the flexibility desired. If the 'advanced' logic is in the VM, advanced controls must be shown for all views or no views; you can't show them for one and hide them for another. This, IMO, breaks the principles of having a VM in the first place.
(Thanks to all who posted here; it's been helpful!)

Related

The MVVM way of presenting controls dynamically

I have a need to display some kind animation as different processes gets started. My initial idea was to simply add some <ContentControl> tags to the XAML and bind them to a property in the View Model object which then simply assigned this property a ProgressBar, some busy spinner or whatever.
This works but I don't like it. The primary reason I don't like it is because the View Model should not involve itself in presentation matters and this pattern clearly breaks that paradigm.
This is pretty much what my (ugly) code looks like atm:
XAML:
<ContentControl Content="{Binding ProcessAAnimation}" />
In View Model class:
public object ProcessAAnimation
{
get { return _processAAnimation; }
private set
{
_processAAnimation = value;
OnPropertyChanged("ProcessAAnimation");
}
}
public object IsProcessARunning
{
get { return _processARunning; }
private set
{
if (value == _processARunning)
return;
_processRunnings = value;
if (value)
ProcessAAnimation = SomeNiftyAnimationControl();
else
{
if (ProcessAAnimation is IDisposable)
((IDisposable)ProcessAAnimation).Dispose();
ProcessAAnimation = null;
}
}
}
// (clipped: More properties for "Process B", "Process C" and so on)
So, is there a better pattern to achieve this. Preferrably, a pattern where I can create my animation controls dynamically using XAML alone?
Please note that I have already tested a solution where I declare three different animation controls and then bind their Visibility property to the View Model state. That, however, is below par in my book because I don't want to just hide the controls, I want them to be gone unless needed. Besides, that would also make it impossible to dynamically use different types of animations for whatever needs may be.
Anyone?
Well your ViewModel knows about the operation and the progress itself. The rest can be accomplished via Triggers. At least thats the way we do it. So your ViewModel has a property "IsLoadingImage" for example, which is set when your viewmodel starts a BackgroundWorker for loading a big image, it also returns the progress reported by the BackgroundWorker "ImageLoadingProgress" now these two properties are enough to pass to your View. Your view, consists of a Progress bar or a custom control for your special animation. You could now bind the "IsLoadingImage" in a Trigger to toggle the ProgressBar/Animation control visibility and the Value of these is bound to "ImageLoadingProgress".
Like i said, thats how we handle it, and our application makes heavy use of MVVM.
Edit respond to a comment: How to change the template in a trigger
<ControlTemplate x:Name="ActiveTemplate" TargetType="{x:Type MyType}">
<!-- Template when active -->
</ControlTemplate>
<ControlTemplate x:Name="DeactivatedTemplate" TargetType="{x:Type MyType}">
<!-- Template when deactivated -->
</ControlTemplate>
<Style TargetType="{x:Type MyType}">
<Setter Property="Template" Value="{StaticResource DeactivatedTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="True">
<Setter Property="Template" Value="{StaticResource ActiveTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
This assumes that MyType is a control that can has a ControlTemplate and that the DataContext has a Property IsActive to toggle the Template.

WPF button styling depending on active view

I'm using Caliburn Micro to develop a simple MVVM WPF application.
My ShellView has a single ContentControl and three buttons each which bind to a public method in my ShellViewModel, lets say ActivateView1, ActivateView2 and ActivateView3.
My ShellViewModel inherits from Conductor and each Activate method calls ActivateItem(new View1ViewModel()), etc.
So far so good. When I click a button, a new view gets activated in the ContentControl. The problem is that I need each button to change style when its "associated view" is active and I have really no idea how to achieve this functionality. Do you have any suggestions?
I'm fairly new to Caliburn Micro and WPF-styling so any help will be much appreciated.
I am not very sure about this but still I can think of something like this,
you can create a style and add the style to your button. something like this
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" TargetName="yourButtonName" Value="Black"/>
<Setter Property="Margin" TargetName="yourButtonName" Value="5,0,5,0"/>
</Trigger>
</Style.Triggers>
</Style>
and you can add this style to your button.
I can think of two possible options you could use:
You could bind your buttons style properties to properties on you ShellViewModel. In those properties you can determine the style to return based on the shells Active view i.e.
return ActiveItem == button1ViewModel ?
(Style) App.Current.Resources["Button1ActiveStyleKey"] :
(Style) App.Current.Resources["Button1InactiveStyleKey"];
this would mean your ViewModel would be aware of Styles which you may not want. If that is the case option two would be to write a Caliburn.Micro IResult which changes the style of the button and return 3 of those (one for each button) from a Coroutine that is invoked via the button click i.e.
public IEnumerable<IResult> ButtonOneClicked()
{
yield return new ChangeButtonStyle("Button1Name", "Button1ActiveStyleKey");
yield return new ChangeButtonStyle("Button2Name", "Button2InactiveStyleKey");
yield return new ChangeButtonStyle("Button3Name", "Button3InactiveStyleKey");
}
The implementation of the ChangeButtonStyle IResult would search the view (provided via the ActionExecutionContext parameter to IResult.Execute) for a control with the name provided to the 1st parameter of ChangeButtonStyle ctor, and then set the style property of that control using the resource key provided as the 2nd paramter to the ChangeButtonStyle ctor.
You can use
<Trigger Property ="IsPressed" Value ="True">
I think it does the trick...

In WPF, how do I find an element in a template that's switched in via a trigger?

I have a UserControl (not a lookless custom control) which, depending on some custom state properties, swaps in various ContentTemplates, all defined as resources in the associated XAML file. In the code-behind, I need to find one of the elements in the swapped-in ContentTemplates.
Now in a lookless control (i.e. a custom control), you simply override OnApplyTemplate then use FindName, but that override doesn't fire when the ContentTemplate gets switched by a trigger (...at least not for a UserControl. I haven't tested that functionality with a custom control.)
Now I've tried wiring up the Loaded event to the control in the swapped-in template, which does fire in the code-behind, then I simply store 'sender' in a class-level variable. However, when I try to clear that value by subscribing to the Unloaded event, that doesn't fire either because the tempalte gets swapped out, thus unwiring that event before it has a chance to be called and the control unloads from the screen silently, but I still have that hung reference in the code-behind.
To simulate the OnApplyTemplate functionality, I'm considering subscribing to the ContentTemplateChanged notification and just using VisualTreeHelper to look for the control I want, but I'm wondering if there's a better way, hence this post.
Any ideas?
For reference, here's a very-stripped-down example of the control I have. In this example, if IsEditing is true, I want to find the textbox named 'FindMe'. If IsEditing is false which means the ContentTemplate isn't swapped in, I want to get 'null'...
<UserControl x:Class="Crestron.Tools.ProgramDesigner.Controls.EditableTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Crestron.Tools.ProgramDesigner.Controls"
x:Name="Root">
<UserControl.Resources>
<DataTemplate x:Key="EditModeTemplate">
<TextBox x:Name="FindMe"
Text="{Binding Text, ElementName=Root}" />
</DataTemplate>
<Style TargetType="{x:Type local:EditableTextBlock}">
<Style.Triggers>
<Trigger Property="IsEditing" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditModeTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<TextBlock x:Name="TextBlock"
Text="{Binding Text, ElementName=Root}" />
</UserControl>
Aaaaaaand GO!
M
Unfortunately, there isn't a better way. You can override the OnContentTemplateChanged, instead of hooking up to the event.
You would need to use the DataTemplate.FindName method to get the actual element. The link has an example of how that method is used.
You would need to delay the call to FindName if using OnContentTemplateChanged though, as it is not applied to the underlying ContentPresenter immediately. Something like:
protected override void OnContentTemplateChanged(DataTemplate oldContentTemplate, DataTemplate newContentTemplate) {
base.OnContentTemplateChanged(oldContentTemplate, newContentTemplate);
this.Dispatcher.BeginInvoke((Action)(() => {
var cp = FindVisualChild<ContentPresenter>(this);
var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox;
textBox.Text = "Found in OnContentTemplateChanged";
}), DispatcherPriority.DataBind);
}
Alternatively, you may be able to attach a handler to the LayoutUpdated event of the UserControl, but this may fire more often than you want. This would also handle the cases of implicit DataTemplates though.
Something like this:
public UserControl1() {
InitializeComponent();
this.LayoutUpdated += new EventHandler(UserControl1_LayoutUpdated);
}
void UserControl1_LayoutUpdated(object sender, EventArgs e) {
var cp = FindVisualChild<ContentPresenter>(this);
var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox;
textBox.Text = "Found in UserControl1_LayoutUpdated";
}

How to select ListBoxItem upon clicking on button in Template?

I have the following Data Template applied to a ListBox:
<DataTemplate x:Key="MyTemplate" DataType="{x:Type DAL:Person}">
<StackPanel Orientation="Horizontal">
<Button Content="X" Command="{x:Static cmd:MyCommands.Remove}"/>
<TextBlock Text="{Binding Person.FullName}" />
</StackPanel>
</DataTemplate>
When I click on the button the command gets fired but the ListBoxItem doesn't get selected. How do I force it to get selected, so that I can get the selected item in my "executed" method?
Thanks
A better way, since you're not really interested in selecting the item (because it will quickly get deleted anyway) would be to pass the item itself to the Command as a CommandParameter.
Alternatively, you can go about in a roundabout manner either with code-behind or with triggers, but I don't think it would be as to the point. For example:
you could handle the ButtonBase.Click event on your listbox, like
<ListBox ButtonBase.Click="lb_Click"
...
then in your code behind, do this:
private void lb_Click(object sender, RoutedEventArgs e)
{
object clicked = (e.OriginalSource as FrameworkElement).DataContext;
var lbi = lb.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
lbi.IsSelected = true;
}
That gets the clicked bound item, because the datacontext of the button is inherited from it's templated item, then the actual autogenerated ListBoxItem from the ListBox's ItemContainerGenerator, and sets the IsSelected property to true. I think that's one of the fastest and easiest ways. Also works with multiple ButtonBase-derived objects in the template.
Of course you can also more nicely encapsulate all this (more or less exactly the same) as a reusable Behavior:
public class SelectItemOnButtonClick : Behavior<ListBox>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler), true);
}
protected override void OnDetaching()
{
this.AssociatedObject.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler));
base.OnDetaching();
}
private void handler(object s, RoutedEventArgs e)
{
object clicked = (e.OriginalSource as FrameworkElement).DataContext;
var lbi = AssociatedObject.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
lbi.IsSelected = true;
}
}
You can use it like this:
<ListBox xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" ...>
<i:Interaction.Behaviors>
<local:SelectItemOnButtonClick />
</i:Interaction.Behaviors>
</ListBox>
Add error handling code like at least null checks, of course - wouldn't want a simple thing like this bombing your app.
To understand the problem, the button sets the Handled property to true for all the mouse events that act on it (MouseDown/Click) so they aren't being considered by the ListBoxItem. You could also attach the MouseDown event to the ListBox and walk the visual tree upwards until you reach the parent ListBoxItem but that's a lot more tricky... eh if you're curious, you can read this article to know why, basically you'll also encounter FrameworkContentElements (which also respond to MouseDown) so the code will get more complicated, with the upside that anything clicked inside the datatemplate will trigger the ListBoxItem to be selected, regardless of whether it marked the event as handled.
Heh, I also tried to do it exclusively with styles and triggers but it got ugly fast and I lost interest (and lost track of all the... err thingies). Basically it could be solved, I think, but I reaaaly don't think it's worth the bother. Maybe I overlooked something obvious though, don't know.
Make the underlying object expose a RemoveCommand property, and bind the button's Command property to it. This simplifies the data template; it also greatly simplifies the case where application logic may dictate that a specific item can't be removed.
Alex, thanks for answer. Your solution with Behavior is great. First solution is not so good because that will work only if you click on specific Button. Here is one more solution that will work on click on arbitrary control form ListBoxItem template:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem"
BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
That is XAML only approach. I also set BasedOn property just to be sure to not override the current ListBoxItem style.

Why does ItemContainerGenerator return null?

I have a ListBox, and I need to set its ControlTemplate to a Virtualizing WrapPanel which is a class that extends VirtualizingPanel, using a style that looks like this:
<Style TargetType="{x:Type ListBox}" x:Key="PhotoListBoxStyle">
<Setter Property="Foreground" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}" >
<s:VirtualizingVerticalWrapPanel>
</s:VirtualizingVerticalWrapPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now, in the private method of Virtualizing WrapPanel below I try to access this.ItemContainerGenerator, but I get null value, any idea what's the problem ??
private void RealizeFirstItem()
{
IItemContainerGenerator generator = this.ItemContainerGenerator;
GeneratorPosition pos = generator.GeneratorPositionFromIndex(0);
using (generator.StartAt(pos, GeneratorDirection.Forward))
{
UIElement element = generator.GenerateNext() as UIElement;
generator.PrepareItemContainer(element);
this.AddInternalChild(element);
}
}
I think I had a similar problem and this helped:
var necessaryChidrenTouch = this.Children;
IItemContainerGenerator generator = this.ItemContainerGenerator;
... for some reason you have to "touch" the children collection in order for the ItemContainerGenerator to initialize properly.
For Windows 8.1 Metro apps, the ItemContainerGenerator was depricated and will return null. New Apis:
ItemsControl.ItemContainerGenerator.ItemFromContainer = ItemsControl.ItemFromContainer
ItemsControl.ItemContainerGenerator.ContainerFromItem = ItemsControl.ContainerFromItem
ItemsControl.ItemContainerGenerator.IndexFromContainer = ItemsControl.IndexFromContainer
ItemsControl.ItemContainerGenerator.ContainerFromIndex = ItemsControl.ContainerFromIndex
http://msdn.microsoft.com/en-us/library/windows/apps/dn376326.aspx
Falck is mostly correct. Actually, you need to reference the 'InternalChildren' of the virtualized stack panel. The decompiled code for this property is:
protected internal UIElementCollection InternalChildren
{
get
{
this.VerifyBoundState();
if (this.IsItemsHost)
{
this.EnsureGenerator();
}
else if (this._uiElementCollection == null)
{
this.EnsureEmptyChildren(this);
}
return this._uiElementCollection;
}
}
The 'EnsureGenerator' does the work of making sure that a generator is available. Very poor 'just in time' design, IMO.
Most probably this is a virtualization-related issue so ListBoxItem containers get generated only for currently visible items (e.g. https://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel(v=vs.110).aspx#Anchor_9)
I'd suggest switching to ListView instead of ListBox - it inherits from ListBoxand it supports ScrollIntoView() method which you can utilize to control virtualization;
targetListView.ScrollIntoView(itemVM);
DoEvents();
ListViewItem itemContainer = targetListView.ItemContainerGenerator.ContainerFromItem(itemVM) as ListViewItem;
(the example above also utilizes the DoEvents() static method explained in more detail here; WPF how to wait for binding update to occur before processing more code?)
There are a few other minor differences between the ListBox and ListView controls (What is The difference between ListBox and ListView) - which should not essentially affect your use case.
This is because you changed the Template of the Listbox, while u should have just changed the ItemsPanel:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<s:VirtualizingVerticalWrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Resources