Wpf collapse item in ItemsControl with animation (slideToggle for example) - c#

I have:
<ItemsControl ItemsSource="{Binding Blocks}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Class Block has properties string Name and bool Visible.
What to add to this xaml to animate item disappearingand appearing when Visible property is changed? (for example by decreasing/increasing Height of item - slideToggle)

Related

ContentPresenter with ContentTemplateSelector inside an ItemTempate of an ItemsControl

So, I have an ItemsControl with an ItemTemplate to visualize the items in it's ItemsSource.
This Template is a DataTemplate with some controls in it.
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock/>
<TextBlock/>
<!-- Content depending on item type -->
<ContentPresenter Content="{Binding}" ContentTemplateSelector="{StaticResource TemplateSelector}"/>
<TextBlock/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The items in the ItemsSource can be of different types. Most of the properties of the different types are the same but some properties are different. So I want to put the common content directly inside the DataTemplate but for the content that differs I want to have some kind of placeholder that displays only type-specific content.
So I tried a ContentPresenter with a ContentTemplateSelector. But this does not work. When I set the Content, the TemplateSelector is never called and the name of the underlying viewmodel is displayed. When I do not set the Content, the TemplateSelector is called, but the item in the SelectTemplate function is null.
I do not want to make the whole DataTemplate for each DataType because most of the content is the same and I would have much duplicate code.
You do not need a DataTemplateSelector.
Just set the DataType of different DataTemplates in the ItemsControl's Resources. The DataTemplates would automatically be selected according to the type of the item.
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:Item1}">
<TextBlock Text="{Binding Item1Property}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Item2}">
<TextBlock Text="{Binding Item2Property}"/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock ... />
<ContentPresenter Content="{Binding}"/>
<TextBlock ... />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

WPF : adding a Button as "Show More" where on click of it would load remaining items in the itemControl which is already in Binding a Collection

I am bit new to WPF (XAML) and I have an ItemsControl bound to a list with a MyCollections property . Now my requirement is initially to show only 1st element from the list and having a Show More button option at the end of list. A click on it would show the rest of the items from the collection.
This is my XAML so far, displaying the whole collection:
<ItemsControl x:Name="ContentRoot" ItemsSource="{Binding MyCollections}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<grid>
<TextBox Text="{Binding }" />
<TextBox Text="{Binding }" />
</grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can have both a ContentControl (which displays the first item of the collection) and an ItemsControl (which displays the whole collection) displayed only when a ToggleButton is checked for example.
<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<DataTemplate DataType="{x:Type local:MyViewModel}">
<Grid>
<TextBox Text="{Binding}" />
</Grid>
</DataTemplate>
</StackPanel.Resources>
<ContentControl Content="{Binding MyCollection[0]}"/>
<ToggleButton x:Name="toggle" Content="Show more"/>
<ItemsControl ItemsSource="{Binding MyCollection}" Visibility="{Binding ElementName=toggle, Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
You can then adapt this to fit your exact needs.

Change visibility of one itemscontrol via checkbox in another

I have two ItemsControls with the same ItemsSource. One has some controls for each item, the other has a checkbox for each item. The controls in them are dynamically added. How can I bind the visibility of the first ItemsControls to the corresponding checkbox in the other ItemsControls ?
Here's the first ItemsControl containing multiple TextBlocks in the row. Note: I want to hide the whole row of controls.
<ItemsControl ItemsSource="{Binding VehicleCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock />
<TextBlock />
<TextBlock />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here's the second ItemsControl with the checkboxes:
<ItemsControl ItemsSource="{Binding VehicleCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<CheckBox Content="{Binding Name}"
IsChecked="True" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
So what happens is, for each item in VehicleCollection, a new row of textblocks is added for the first ItemsControl, and a checkbox is added for the second ItemsControl. These should relate to each other for example: If I uncheck the first checkbox, the first row for the other ItemsControl should be hidden.
I know how to do the booltovis converter, just not sure on how to relate these two ItemsControls.
Edit: These are both in the mainwindow.xaml by the way.
In order to show or hide an item of the first ItemsControl, add an IsVisible property to your Vehicle class (i.e. the element type of the VehicleCollection) and bind the Visibility of the item's ContentPresenter to the IsVisible property in an ItemContainerStyle:
<ItemsControl ItemsSource="{Binding VehicleCollection}">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Visibility"
Value="{Binding IsVisible,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In the other ItemsControl, bind the IsChecked property of the CheckBox to the IsVisible property:
<ItemsControl ItemsSource="{Binding VehicleCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsVisible}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Of course make sure that class Vehicle implements INotifyPropertyChanged and fires the PropertyChanged event when the IsVisible property changes.
You should be able to manage this by adding a boolean Property in your Vehicle class, which I presume is the basis for your VehicleCollection. Something like: IsSelected.
Then you can modify your XAML as shown below:
<ItemsControl ItemsSource="{Binding VehicleCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Visibility="{Binding IsSelected,Converter={StaticResource boolToVisConverter}}"/>
<TextBlock Visibility="{Binding IsSelected,Converter={StaticResource boolToVisConverter}}"/>
<TextBlock Visibility="{Binding IsSelected,Converter={StaticResource boolToVisConverter}}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding VehicleCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<CheckBox Content="{Binding Name}"
IsChecked="{Binding IsSelected}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is untested XAML, as I just typed it into the answer. Might need a tweak.

Binding a ListBox to the elements of a collection in a collection

I have some problems binding a ListBox to the elements of a collection in a collection.. Let me explain:
I have a collection, ObservableCollection<Test> named testsCollection. Every test contains an ObservableCollection<LogEvent> named LogEvents. Every LogEvent has a Message that I need to display in the ListBox.
I need to display every "Message" in every "LogEvent" in every "Test". It has to be displayed in a flat list so I'm using a ListBox.
Here's a summary of what I tried:
DataContext = testCollection; // testCollection is an ObservableCollection<Test>
The I put this in the XAML:
<ListBox ItemsSource="{Binding LogEvents}" ItemTemplate="{StaticResource stepItemTemplate}">
Finally, here's the ItemTemplate, stepItemTemplate:
<DataTemplate x:Key="stepItemTemplate">
<TextBlock Text="{Binding Message}"></TextBlock>
</DataTemplate>
This "works" but it only displays the Messages in the LogEvents of the first Test. But I need to display every Messages of every LogEvent of every Test.. And I don't know what to try anymore :(
You should Usser ItemsControl when you want to bind a scenario like this
<ListBox ItemsSource="{Binding testsCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Message}" FontSize="20" />
<ItemsControl ItemsSource="{Binding LogEvents}" Margin="0 20 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Blue" BorderThickness="2">
<TextBlock Text="{Binding Message}" FontSize="20" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>

WPF Binding to parent ItemsControl from inside of child ItemsControl data template

I need to be able to bind to a parent ItemsControl's properties from inside of a child ItemsControl data template:
<ItemsControl ItemsSource="{Binding Path=MyParentCollection, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Path=MySubCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=MyParentCollection.Value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Let's assume that MyParentCollection (the outer collection) is of the following type:
public class MyObject
{
public String Value { get; set; }
public List<MyChildObject> MySubCollection { get; set;
}
And let's assume that MyChildObject from the above class is of the following type:
public class MyChildObject
{
public String Name { get; set; }
}
How can I bind to MyParentCollection.Value from inside of the inner data template? I can't really use a FindAncestor by type because they both all levels use the same types. I thought maybe I could put a name on the outer collection and use an ElementName tag in the inner binding, but that still couldn't resolve the property.
Any thoughts?
saving the parent item in the tag of the child itemscontrol could work
<DataTemplate>
<ItemsControl ItemsSource="{Binding Path=MySubCollection}" Tag="{Binding .}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Tag.Value, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
its not tested but give you a hint in the right direction :)
Binding for Tag, as suggested in the other answer, is not required. All data can be obtained from DataContext of ItemControl (and this markup Tag="{Binding}" just copies DataContext into Tag property, which is redundant).
<ItemsControl ItemsSource="{Binding Path=MyParentCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Path=MySubCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DataContext.Value, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Categories

Resources