how to set the generated item for itemscontrol in databinding? - c#

if I'm using a ListBox for data binding, the listbox generates a listboxitem for each item, same goes for combo box and comboBoxItem.
My question is - how do i set it myself for a given ItemsControl? (e.g. make the containing element be Border)?

The default item used to wrap each item is a ContentPresenter
I am not sure why you'd want to overwrite this, since it has no visual appearance or specific behavior that would mess with your UI.
You can set the ItemTemplate if you want to wrap each item in a Border object
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Blue" BorderThickness="2">
<TextBlock Text="{Binding }" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
Or set the ItemContainerStyle if you want to apply any specific style to the ContentPresenter
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>

After digging a little with ILSpy -
Apparently The magic is done in
protected override DependencyObject GetContainerForItemOverride()
{
return new ListBoxItem();
}
This is where ListBox does it - and in my control I should override this as well.

Related

WPF MVVM Binding dynamic control in code behind and pass in View

I am working on WPF application using MVVM. I have two page. I have multiple UserControls in a page 1, on selection of UserControls from page 1, I want to show that selected userControl in 2nd page. Below are my code.
ViewModel Code
public RelayCommand<string> OnClickSelectWidgetCommand => new RelayCommand<string>((setUserControlName) =>
{
using (new CursorWait())
{
var MyContentControl = setUserControlName;
MessageBox.Show(MyContentControl);
//How to render UserControl to View?
}
}, true);
Here in above code I get the UserControl name in setUserControlName variable. Now how to bind that UserControl to XAML page? Below are my code that I have tried.
View Code
<StackPanel Background="Black" VerticalAlignment="Top">
<Border Name="UserControl1BorderLow" BorderBrush="White" BorderThickness="0" >
<ItemsControl ItemsSource="{Binding LowCollection}" Margin="4,0" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserControlColumn1XL HorizontalAlignment="Left" Margin="2" />
<!--what can I do here in above line to make it dynamically render the userControl in place of UserControlColumn1XL-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border></StackPanel>
Above code, In DataTemplate what need to be change to bind UserControls dynamically?
There are two ways to solve this, one involves setting the template based on your data type (DataTemplates) and the second involves setting it based on the data itself (DataTriggers).
In the first case your LowCollection should be an array of objects, or some base class that your view models are all derived from (ViewModel1, ViewModel2 etc). In this case you can get rid of your itemtemplate altogether and just add DataTemplates to specify how each of the items in your ItemsControl should be represented:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:ViewModel1}">
<UserControl1 />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<UserControl2 />
</DataTemplate>
... etc...
In the second case you need to set a template based on the value of some property in your view model. In this case you do need to set the ItemTemplate, and you give it a Style which uses data triggers to set an appropriate DataTemplate:
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Style.Triggers>
<DataTrigger Binding="{Binding YourProperty}" Value="YourValue1">
<Setter Property="ContentTemplate" Value="{StaticResource YourDataTemplate1}" />
</DataTrigger>
<DataTrigger Binding="{Binding YourProperty}" Value="YourValue2">
<Setter Property="ContentTemplate" Value="{StaticResource YourDataTemplate2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</DataTemplate>
</ItemsControl.ItemTemplate>
The relevant parts to note here are that there is a property in your view model called YourProperty which can have two values i.e. YourValue1 or YourValue2; the style above then selects either YourDataTemplate1 or YourDataTemplate2, depending on the value of YourProperty.

Hide checkbox, but show its content

Is it possible to hide a checkbox, but leave its content visible?
<ListBox
ItemsSource ="{Binding MyItemCollection}"
SelectionMode="Single"
Width="300"
Height="320">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}">
<CheckBox.Content>
<TextBlock Text="{Binding Item.Code}"/>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<CheckBox Content="Edit Mode"
IsChecked="{Binding Path=EditModeSelected, Mode=TwoWay}">
</CheckBox>
</StackPanel>
I would like to hide the checkboxes in the list box when I turn Edit Mode off (so it should be binded to EditModeSelected), but the text should be left visible.
In order to do so You can keep two TextBlocks. In edit mode visible CheckBox and hide TextBlock and in reader mode vice versa. I hope this may help. As DataTemplate can have only one child here's the fix
Create a Window Resource like below. Two Data Templates were created one for edit mode and another for Reader Mode.
<Window.Resources>
<DataTemplate x:Key="EditModeTemplate">
<CheckBox IsChecked="{Binding IsChecked}">
<CheckBox.Content>
<TextBlock Text="{Binding Item.Code}"/>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="ReaderModeTemplate">
<TextBlock Text="{Binding Item.Code}"/>
</DataTemplate>
</Window.Resources>
Now in .cs file assign the Date Template as per requirements.
if (EditMode)
{
DemoCollection.ItemTemplate = this.Resources["EditModeTemplate"] as DataTemplate;
}
else
{
DemoCollection.ItemTemplate = this.Resources["ReaderModeTemplate"] as DataTemplate;
}
3 possible solutions come in my mind - two of them more or less "hacks" and one more or less clean solution:
A checkbox and textblock for every item - you can get problems with margins etc
A checkbox with no content (which is only visible when in edit mode), and a textblock which is always visible
Take the default controltemplate for checkbox (Default ControlTemplate for CheckBox) and bind the visibility of the checkbox
Here is a Xaml only solution pulled from a project I am working on. In this case "ShowCheck" is a field in the current binding context saying whether or not to show the check.
<CheckBox Content="{Binding Name}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowCheck}" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<ContentControl Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Basically if the checkbox should be invisible, then I use a style and a trigger to change the checkbox's template to something without the checkbox. My implementation the content is just a string, so this works. If you were putting more complicated objects into the checkbox, you might need to shuttle the ContentTemplate, ContentTemplateSelector, and related fields into the ContentControl that is used to replace the checkbox

ListView.SelectedItems does not change when clicking a TextBox in the ItemTemplate

I have a ListView bound with an ObservableCollection. The Itemtemplate of the ListView is the following:
<ListView ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" PreviewMouseLeftButtonUp="ListViewClicked" PreviewKeyDown="NameBox_KeyDown">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="./Resources/DeleteIcon.png"/>
<TextBox Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This way I give the user the choice to either change the name of the element, or delete it from the list. I handle this two operations in code behind through the two event handlers attached to the ListView.
The problem is that when I click on the TextBox, the SelectedItems property of the ListView doesn't change and it points the last selected item. When I click on the image or on the free space around the selection change.
Now I have two questions:
Is there an easy way to fix this behavior?
Is it possible to get a reference to the collection item whose property is exposed by the TextBox?
One way to deal with this problem is to set Trigger to set IsSelected when keyboard focus is within ListViewItem
<ListView ... SelectionMode="Single">
<!-- .... -->
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>

WPF Tab Data Template - Controls retain values when new tabs are created

I have a problem with the WPF tab control.
I have a TabControl, with the ItemsSource bound to an ObservableCollection. I created a data template for the header/content portion of the tabs. The content portion contains a custom control, with a bunch of labels and text boxes. For the text boxes that are editable when a new tab is created that data carries over and appears in the new tab. Not sure if it's a problem with my XAML or something in the view model. Here's my code for the XAML:
<UserControl.Resources>
<DataTemplate x:Key="TabItemHeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding AdFile.Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TabItemContentTemplate">
<MyView:MyCustomControl/>
</DataTemplate>
<Style x:Key="TabItemContainerStyle" TargetType="TabItem">
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="HeaderTemplate"
Value="{StaticResource TabItemHeaderTemplate}"/>
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="ContentTemplate"
Value="{StaticResource TabItemContentTemplate}"/>
</Style>
</UserControl.Resources>
<TabControl Grid.Row="3" ItemsSource="{Binding OpenedFiles}" x:Name="_myTabControl" SelectedItem="{Binding Path=CurrentDataControlViewModel, Mode=TwoWay}" SelectionChanged="TabControlSelectionChanged" ItemContainerStyle="{StaticResource TabItemContainerStyle}"/>
Not sure what other information I should provide. Maybe this is a common problem and I am just not setting something up correctly? Basically I just want to be able to create a new instance of the control for every tab...
Thanks in advance.
It sounds as though your ViewModel is a Singleton, being cloned, or you're trying to populate the new tab with the existing ViewModel.
If you're using MEF, remember to set the [PartCreationPolicy] attribute to NonShared.

ItemsControl DataContext Binding Error

I have already looked into this solution: Show if ItemsControl.ItemsSource is null.
I set the DataContext of the ItemsControl via codebehind to an ObservableCollection. Everything works fine except that it only resolves once during the loading phase. If the items control has a few items in the start, the text disappears but doesn't appear later onwards. If it's empty, the text appears, but it doens't go away when i add items later on. I have tried ItemsSource as well but no luck. I'm aware im using a control template as of now, and i can use relative source TemplatedParent but i just wanted to make sure. Upon further testing, the converter function doesn't seem to activate after i try to add/remove items in the list even though the items show on my itemscontrol.
<ItemsControl x:Name="MedicationList" ItemTemplate="{StaticResource UserTemplate}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Grid>
<TextBlock Text="No Items to Display" Visibility="{Binding DataContext, ElementName=MedicationList, Converter={StaticResource AnyItemsToVisibilityConverter}}" />
<ItemsPresenter />
</Grid>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
What are you using as the datacontext/itemssource? If it is an ObservableCollection as I would expect, then you would be best off binding to its "Count" property and then using a trigger to collapse the text block when necessary.
The reason that the binding isn't currently updating is that the DataContext itself isn't actually changing. Properties on the DataContext ARE changing, so if you bind to the correct property (count) your bindings will update.
This code snippet should work:
<ControlTemplate TargetType="ItemsControl">
<Grid>
<TextBlock x:Name="txtBlock" Text="No Items to Display" Visibility="Collapsed" />
<ItemsPresenter />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Count}" Value="0">
<Setter TargetName="txtBlock" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
By using a data trigger you can avoid the need for a converter to convert a numeric value into a visibility and keep everything in your .xaml.

Categories

Resources