Change ItemTemplate of XAML Listview programmatically in backend - c#

I've read so many postings here on StackOverflow and lots of blog entries but could not find a working answer to my question:
I'm developing a windows 10 universal app. I have a listview using an item template. Now I added another item template and want to set with template to use when the application starts. No need to do it item by item, the template should be added for all items.
<Page.Resources>
<DataTemplate x:Key="DataTemplate1">
(...)
</DataTemplate>
<DataTemplate x:Key="DataTemplate2">
(...)
</DataTemplate>
</Page.Resources>
<ListView
x:Name="itemListView"
AutomationProperties.AutomationId="ItemsListView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.Column="0"
IsSwipeEnabled="False" HorizontalContentAlignment="Stretch"
SelectionChanged="ItemListView_SelectionChanged" IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding FeedItems}" ItemTemplate="{StaticResource DataTemplate1}" Margin="0,60,0,10" Grid.RowSpan="2">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
What's the easiest way to do it? I've tried so many options, none of them did work :-(
Many thanks!

You can set the ItemTemplate in code behind:
public MainPage()
{
this.InitializeComponent();
itemListView.ItemTemplate = (DataTemplate)Resources["DataTemplate2"];
}

bit old question but first in google search, so , i add my answer
you may access template by name
define x:name for resource
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="dtPerMeal" x:Name="dtPerMeal">
<TextCell Text="{Binding Product.Name}" Detail="{Binding TotalWeight}" />
</DataTemplate>
<DataTemplate x:Key="dtPerBatch" x:Name="dtPerBatch">
<TextCell Text="{Binding Product.Name}" Detail="0" />
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
and then access it by name in code behind
(switching datatemplate on a radiobutton change )
private void rbMeal_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
lvProducts.ItemTemplate = (rbMeal.IsChecked) ? dtPerMeal : dtPerBatch;
}

Related

An error occurred when using binding in C#

<ListView Margin="6,6,5,7" x:Name="lvBB" Foreground="Black" >
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Vertical" Margin="10" Background="{Binding SelectedNotamColor}">
<WrapPanel Orientation="Horizontal">
<TextBlock Text="{Binding Priority}" FontWeight="Bold" FontSize="18"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="{Binding SelectedNotamColor, mode=Toway}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
I am binding a json object which has the property called SelectedNotamColor, if I use it in the ListView.ItemContainerStyle setter, when I try to save my json object into a text file it will occur an error like this
System.InvalidOperationException:the object is being used
and when I delete the setter property, it will come back to normal.
How to solve this problem or is there any other way to bind my ListView items' color?
You have syntax error in your style setter property.
Try to replace
mode=Towway
with
mode=TwoWay
I coudn't find line of code where you fill items of your listbox.
For example, in XAML:
ItemsSource="{Binding YourListOfJsonObjects}
or code behind:
lvBB.ItemsSource = YourListOfJsonObjects

Dynamically create ContextMenu

In my application I want to dynamically build up a context-menu. The first MenuItem is static and the second one should be a Separator. All items after the Separator are dynamically created at runtime.
I don't want to use code-behind becaus I'm working with the MVVM-Pattern.
My idea now was to create an interface called IAppMenuItem with the following three implementations
ModifyMenuItem (Static MenuItem)
SeparatorMenuItem
ExecuteMenuItem (Dynamic MenuItem
In the viewmodel of my application I've created an ObservableCollection<IAppMenuItem> which contains the ContextMenu-Items.
Until here everything works fine. My problem is the presentation of the ContextMenu-Items in the UI.
I tried to set the correct controls with the following DataTemplates in the Resources of the view.
<DataTemplate DataType="{x:Type model:SeparatorMenuItem}">
<Separator/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
<MenuItem Header="Edit items"/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
<MenuItem Header="{Binding DisplayText}"/>
</DataTemplate>
The definition of my ContextMenu is just:
<ContextMenu ItemsSource="{Binding MenuItemsCollection}"/>
The DataTemplates are working fine, but the controls are drawn inside a MenuItem. So for example for the Separator I see in the UI a Separator-Control inside a MenuItem-Control. But I need the Separator to be the Control.
Anyone have an idea how to set the Controls inside the DataTemplates directly to the contextmenu?
Update:
The complete ContextMenu looks like:
<ToggleButton Margin="0,0,10,0"
AutomationProperties.Name="Update"
AutomationProperties.AutomationId="Update_List"
Content="Update"
AttachedProperties:ButtonExtensions.IsDropDownButton="True"
Style="{StaticResource GenericToggleButtonStyle}">
<ToggleButton.ContextMenu>
<controls:CustomContextMenu ItemsSource="{Binding MenuItemsCollection}">
<ContextMenu.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
<MenuItem Header="{Binding DisplayText}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
<MenuItem Header="Edit items"/>
</DataTemplate>
</ResourceDictionary>
</ContextMenu.Resources>
</controls:CustomContextMenu>
</ToggleButton.ContextMenu>
</ToggleButton>
The GenericToggleButtonStyle is just:
<Style x:Key="GenericToggleButtonStyle" TargetType="ToggleButton"
BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="MinWidth" Value="80" />
<Setter Property="Height" Value="22" />
<Setter Property="Padding" Value="3,1" />
</Style>
Here is a screenshot of the MenuItems
It seems when you set the ItemSource property on a ContextMenu the default ItemContainer used is MenuItem. This is why the Separator is rendered as a MenuItem.
You can fix this by implementing your own ContextMenu control that inherits from ContextMenu.
You need to override the IsItemItsOwnContainerOverride and GetContainerForItemOverride methods.
Please see my example below:
public class CustomContextMenu
: ContextMenu
{
private bool _mustGenerateAsSeparator = false;
protected override bool IsItemItsOwnContainerOverride(object item)
{
_mustGenerateAsSeparator = (item is SeparatorMenuItem);
return base.IsItemItsOwnContainerOverride(item);
}
protected override System.Windows.DependencyObject GetContainerForItemOverride()
{
if (_mustGenerateAsSeparator)
{
return new Separator { Style = this.FindResource(MenuItem.SeparatorStyleKey) as System.Windows.Style };
}
else
{
return base.GetContainerForItemOverride();
}
}
}
Style = this.FindResource(MenuItem.SeparatorStyleKey) as System.Windows.Style is needed as you have to apply the default MenuItem.SeparatorStyleKey style to get the default separator style.
This link pointed me into the right direction http://drwpf.com/blog/category/item-containers/
How to use the custom control in XAML:
window declaration :xmlns:cnt="your namespace"
<Label Content="Dynamic Menu">
<Label.ContextMenu>
<cnt:CustomContextMenu x:Name="contextMenu" ItemsSource="{Binding MenuItemsCollection}">
<ContextMenu.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
<MenuItem Header="{Binding DisplayText}"></MenuItem>
</DataTemplate>
<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
<MenuItem Header="{Binding DisplayText}"></MenuItem>
</DataTemplate>
</ResourceDictionary>
</ContextMenu.Resources>
</model:CustomContextMenu>
</Label.ContextMenu>
</Label>
Second Part of the question:
Update your data templates to use TextBlock as they will be rendered inside a MenuItem, please see below:
<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
<TextBlock Header="Edit items"/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
<TextBlock Header="{Binding DisplayText}"/>
</DataTemplate>

WPF TabControl with ContentControl

I have searched a lot on SO and didn't find answer to my problems.
I want to use TabControl with MVVM. Here is how I add TabControl to MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type models:PartnersViewModel}">
<views:PartnersView />
</DataTemplate>
<DataTemplate DataType="{x:Type models:ProjectsViewModel}">
<views:ProjectsView />
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<models:ApplicationViewModel/>
</Window.DataContext>
<TabControl ItemsSource="{Binding PageViewModels}" TabStripPlacement="Left">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
PageViewModels is ObservableCollection<IPageViewModel>.
IPageViewModel is simple interface with one property Name. There are 2 implementation of this interface PartnersViewModel and ProjectsViewModel.
public class ProjectsViewModel : IPageViewModel
{
public String Name
{
get { return "Projects"; }
}
}
public class PartnersViewModel : IPageViewModel
{
public String Name
{
get { return "Partners"; }
}
}
I want each tab to be displayed as ContentControl. Header text is taken from Name property. My ProjectsView and PartnersView looks like this:
<UserControl x:Class="WANIRPartners.Views.ProjectsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
<Grid>
<Label Content="Projects Content" />
</Grid>
</UserControl>
Using this code, header and content in TabControl is exactly the same. 'Projects Content'/'Partners Content' are shown in tab headers and also(this is ok) in tab content. When I change <Label/> to <DataGrid/> tab headers contains datagrid (sic!).
How can I get this working properly. I mean how can I display headers as value of property Name and tab content as properly rendered <views:PartnersView /> or <views:ProjectsView /> depending on what is in PageViewModels.
your code should work, dont have a IDE atm. you can also use Snoop to check your bindings at runtime. i changed the ContentTemplate to:
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding .}" />
</DataTemplate>
</TabControl.ContentTemplate>
and it works. but even your code works in my testapp.
I have discovered solution for Elysium. Adding fter adding code belowe everything work fine.
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="Header" Value="{Binding Name}"/>
</Style>
</TabControl.Resources>

Access a control from within a DataTemplate with its identifying name

In my WPF application I have a ComboBox control that is located inside a Grid Control. In XAML I am assigning a name to the ComboBox:
<DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center" Text="{Binding name_ru}" Width="Auto" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Name="stcom" Style="{DynamicResource ComboBoxStyle}" SelectionChanged="status_SelectionChanged" Height="auto" Width="Auto">
<ComboBox.BorderBrush>
<SolidColorBrush Color="{DynamicResource Color1}"/>
</ComboBox.BorderBrush>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
With the method FindName(string) I am trying to refer to the ComboBox with its associated name:
ComboBox stcom
{
get
{
return (ComboBox)FindName("stcom");
}
}
if (stcom != null)
{
stcom.ItemsSource = list;
}
But obviously the control can not be found because the reference stcom remains null.
The question now is how to refer to my ComboBox using its name property ?
The answer is:
<Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Grid>
<TextBlock Name="tbUserIcon" Text="t1" />
<TextBlock Name="tbCheck" Text="✓" />
</Grid>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and C#:
checkBox.ApplyTemplate();
var tbUserIcon= (TextBlock)checkBox.Template.FindName("tbUserIcon", checkBox);
don't forget the checkBox.ApplyTemplate() be fore Template.FindName() it's important!
First you have to get access to the control template which it has been applied to, then you can find an element of the template by name.
Have a look at the MSDN knowledge base :
How to: Find ControlTemplate-Generated Elements
You can't access controls that are part of a DataTemplate with their name.
You can try to read about some workarounds for example
WPF - Find a Control from DataTemplate in WPF
You can also have a look at the dozens of posts here on SO issuing this topic for example
here
here
here
here
here
here
here
here

C#/XAML/WPF binding working partially, only displays first item in List

I have what should be a really simple binding, but the problem I'm seeing is that instead of displaying the three companies (company_list is a List, where Company contains a company_id to bind to), I see the window pop up with only the first company_id in company_list. I have other bindings which seem to work fine, and in some other cases I see that I've used ItemSource instead of DataContext, but when I use that I get "Items collection must be empty before using ItemsSource". I've searched extensively for a simple answer to this in stackoverflow, msdn and elsewhere, and have seen mostly really complex solutions that I haven't been able to understand/apply.
When my window appears, it has:
CompanyA
where it should have:
CompanyA
CompanyB
CompanyC
which is the content of the company_list (yes, verified in debugger). Suggestions appreciated! Code and XAML follow.
ReadMasterCompanyList(); // populates a_state.company_list with 3 companies
// display company list dialog
CompanySelect cs_window = new CompanySelect();
cs_window.CompanyListView.DataContext = a_state.company_list;
// fails: cs_window.CompanyListView.ItemsSource = a_state.company_list;
cs_window.Show();
And the XAML from CompanySelect:
<Grid>
<ListView IsSynchronizedWithCurrentItem="True"
x:Name="CompanyListView"
SelectionMode="Single" SelectionChanged="CompanyListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="30"/>
</Style>
</ListView.ItemContainerStyle>
<ListViewItem Content="{Binding Path=company_id}"></ListViewItem>
</ListView>
</Grid>
I would set the ItemsSource of the ListView, rather than the DataContext, either in codebehind:
cs_window.CompanyListView.ItemsSource = a_state.company_list;
or with binding:
<ListView ItemsSource="{Binding company_list}">
And then set the ItemTemplate of the ListView instead.
...
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding company_id}" />
</DataTemplate>
</ListView.ItemTemplate>
...
I would also look into using the MVVM design pattern for testability and separation of concerns, and look at using PascalCase for your property names.
Also, unless you specifically wanted a ListView, I would use a ListBox.
First, set the DataContext only after cs_window.Show().
Second, the ListViewItem you have as a child in your ListView's XAML is why you're only seeing one.
Third, might work better (and would be more MVVM-ish) if you define ItemsSource in the XAML, like this:
<ListView ItemsSource="{Binding Path=company_list}" ...>
That's after making a_state the DataContext of the ListView's container or some other ancestor element.
The problem is, that you define one ListViewItem in your XAML code. You shouldn't do this.
Try something like this:
<Grid>
<ListView IsSynchronizedWithCurrentItem="True"
x:Name="CompanyListView"
SelectionMode="Single" SelectionChanged="CompanyListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="30"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Content={Binding Path=company_id}/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

Categories