I'm sure this is simple but i just cant seem to figure out how to do it. Basicly i have a list of customers that comes from a azure mobile service database. so far everything works fine but I would like to set the item template for each item in a listbox based on the data. I have 2 templates, one for companies and on for just a person. My question is how to apply each one.
Templates
<DataTemplate x:Key="CompanyItemTemplate">
-------
</DataTemplate>
<DataTemplate x:Key="CustomerItemTemplate">
-------
</DataTemplate>
Code
CustomerListItems.ItemsSource = customeritems.OrderBy(customer => customer.CustomerName);
foreach (Customers customer in customeritems)
{
if (customer.Company != "")
{
CustomerListItems.ItemTemplate = CompanyItemTemplate;
}
else
{
CustomerListItems.ItemTemplate = CustomerItemTemplate;
}
}
You can use a DataTemplateSelector to dynamically select the DataTemplate based on the data bound to your properties:
Sample code:
public class ImgStringTemplateSelector : DataTemplateSelector
{
public DataTemplate ImageTemplate { get; set; }
public DataTemplate StringTemplate { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
String path = (string)item;
String ext = System.IO.Path.GetExtension(path);
if (System.IO.File.Exists(path) && ext == ".jpg")
return ImageTemplate;
return StringTemplate;
}
}
<Window.Resources>
<local:RelativeToAbsolutePathConverter x:Key="relToAbsPathConverter" />
<DataTemplate x:Key="stringTemplate">
<TextBlock Text="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="imageTemplate">
<Image Source="{Binding Converter={StaticResource relToAbsPathConverter}}"
Stretch="UniformToFill" Width="200"/>
</DataTemplate>
<local:ImgStringTemplateSelector
ImageTemplate="{StaticResource imageTemplate}"
StringTemplate="{StaticResource stringTemplate}"
x:Key="imgStringTemplateSelector" />
</Window.Resources>
<Grid>
<ListView ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding ElementName=This, Path=PathCollection}"
ItemTemplateSelector="{StaticResource imgStringTemplateSelector}">
</ListView>
</Grid>
</Window>
Related
I'm Stuck creating a Master/Detail View in WinUI 3.
screenshot
I Have a treeview as my master list with two different item types
ExplorerItemTypeA and ExplorerItemTypeA each a partial class from the base ExplorerItem
I wish for the detail view to show the correct template for the different type (A&B) so I can bind and edit etc.
The Treeview DataTemplates work fine. Here is the XAML for the details view:
<Page.Resources>
<DataTemplate x:Key="ContentTypeATemplate" x:DataType="local:ExplorerItemTypeA">
<StackPanel Orientation="Horizontal">
<TextBlock Text="A Template"/>
<TextBlock Text="{x:Bind Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ContentTypeBTemplate" x:DataType="local:ExplorerItemTypeB">
<StackPanel Orientation="Horizontal">
<TextBlock Text="B Template"/>
<TextBlock Text="{x:Bind Name}"/>
</StackPanel>
</DataTemplate>
<ExplorerContentTemplateSelector x:Key="ExplorerContentTemplateSelector"
TreeItemTypeATemplate="{StaticResource ContentTypeATemplate}"
TreeItemTypeBTemplate="{StaticResource ContentTypeBTemplate}"/>
</Page.Resources>
If first tried a frame but am now trying a Content presenter for the detail view.
<TreeView x:Name="MasterListView"
Grid.Column="0"
ItemsSource="{x:Bind ViewModel.TemplateItems}"
SelectedItem="{x:Bind ViewModel.SelectedItem,Mode=TwoWay}"
ItemTemplateSelector="{StaticResource ExplorerItemTemplateSelector}"/>
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Column="1"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}"
ContentTemplateSelector="{StaticResource ExplorerContentTemplateSelector}" />
The Selector is
public class ExplorerContentTemplateSelector : DataTemplateSelector
{
public DataTemplate TreeItemTypeATemplate { get; set; }
public DataTemplate TreeItemTypeBTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
var explorerItem = (ExplorerItem)item;
switch (explorerItem.Type)
{
case ExplorerItem.ExplorerItemType.A:
return TreeItemTypeATemplate;
case ExplorerItem.ExplorerItemType.B:
return TreeItemTypeBTemplate;
default:
Console.WriteLine("Default case");
return TreeItemTypeATemplate;
}
}
}
I feel the answer is obvious and elegant, but despite searching for clues and methods this c# beginner is just not getting anywhere.
I've been stuck on this simple problem for far longer than I care to admit.
Thanks to anyone who is willing to give their time.
Replace the ContentPresenter with a ContentControl in your XAML markup:
<ContentControl
x:Name="DetailContentPresenter"
Grid.Column="1"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}"
ContentTemplateSelector="{StaticResource ExplorerContentTemplateSelector}" />
In your DataTemplateSelector, you should also override the SelectTemplateCore overload that accepts a DependencyObject parameter:
public class ExplorerContentTemplateSelector : DataTemplateSelector
{
public DataTemplate TreeItemTypeATemplate { get; set; }
public DataTemplate TreeItemTypeBTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item) =>
SelectTemplateCore(item, null);
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var explorerItem = item as ExplorerItem;
switch (explorerItem?.Type)
{
case ExplorerItem.ExplorerItemType.A:
return TreeItemTypeATemplate;
case ExplorerItem.ExplorerItemType.B:
return TreeItemTypeBTemplate;
default:
Console.WriteLine("Default case");
return TreeItemTypeATemplate;
}
}
}
I have a listView containing different types of items and I need to display them using different elements in UI. e.g. i have children and adult members in listView, and children will not have kids, spouses etc, while adults will have their children, spouses, workplace etc. As far as i know, once i layout structure in XAML using data template, i cannot change it. I created a UserControl for different items, not sure how to use it in ListView when adding items.
Looking for help on how to do this.
Thanks in advance.
Based on your scenario, you could try to use DataTemplateSelector Class. This class enables you to apply different templates for ListView based on your own logic.
Here are the steps that you need to do to implement this:
You will need to create your own DataTemplateSelector Class. Then you could declare each template as a property of the class.
You need to create an instance of your own DataTemplateSelector class in the Resources section of your XAML file. You should create instances of DataTemplate objects and define their layout in the resources section. Then assign these data templates to the template properties you declared in the DataTemplateSelector class.
The final step is that assign the DataTemplateSelector class to the ItemTemplateSelector property of the ListView.
I've made a simple demo and you could refer to the following code:
Code behind:
public sealed partial class MainPage : Page
{
public List<int> NumbersList { get; set; }
public MainPage()
{
this.InitializeComponent();
NumbersList = new List<int>();
for (int i=0;i<10; i++)
{
NumbersList.Add(i);
}
}
}
public class MyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ChildrenTemplate { get; set; }
public DataTemplate AdultTemplateent { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
// use your own conditions
if ((int)item % 2 == 0)
{
return AdultTemplateent;
}
else
{
return ChildrenTemplate;
}
}
}
Xaml:
<Page.Resources>
<DataTemplate x:Key="AdultTemplateent" x:DataType="x:Int32">
<StackPanel Orientation="Horizontal" Background="LightGray">
<TextBlock Text="This is Adult Item" Margin="5"/>
<TextBlock Text="{x:Bind}" Margin="5"/>
<TextBlock Text="Workplace:NewYork" Margin="5"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ChildrenTemplate" x:DataType="x:Int32">
<StackPanel Orientation="Vertical" Background="LightBlue">
<TextBlock Text="This is Children Item" Margin="5" />
<TextBlock Text="{x:Bind}" Margin="5" />
<TextBlock Text="School:DC" Margin="5"/>
</StackPanel>
</DataTemplate>
<local:MyDataTemplateSelector x:Key="MyDataTemplateSelector" AdultTemplateent="{StaticResource AdultTemplateent}" ChildrenTemplate="{StaticResource ChildrenTemplate}"/>
</Page.Resources>
<Grid>
<ListView x:Name = "TestListView"
ItemsSource = "{x:Bind NumbersList}"
ItemTemplateSelector = "{StaticResource MyDataTemplateSelector}">
</ListView>
</Grid>
How it looks like:
You could get more detailed information here: Data template selection: Styling items based on their properties
i have MyPage.xaml and MyPage.xaml.cs files.
Into .xaml file i written a data template like this:
<DataTemplate x:Key="MyDataTemplate" x:DataType="local:MyClass">
...
<TextBlock Text="{x:Bind name}" HorizontalAlignment="Left" TextWrapping="Wrap"/>
...
</DataTemplate>
I can bind name attribute of MyClass correctly.
Now i need to bind an attribute of .xaml.cs file but DataTemplate show me only MyClass data. How can i bind data from .xaml.cs page? Outside DataTemplate (but in the same xaml file) i can see any attribute of .xaml.cs file.
I need to bind a List of objects to a combobox like this:
<ComboBox ItemsSource="{x:Bind myList}" HorizontalAlignment="Left"></ComboBox>
but myList is an .xaml.cs attribute.
I want to view a string name attribute of objects of the list.
Thank you for your help
uwp: how to bind data inside DataTemplate outside of x:DataType?
For your recrement, we suggest you use Binding to replace x:Bind, You could use Binding to access current root DataContext with ElementName.
For example
<Grid x:Name="GridRoot">
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<ComboBox ItemsSource="{Binding ElementName=GridRoot, Path=DataContext.Options}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Code Behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
public List<string> Options { get; set; } = new List<string>() {"One","Two","Three" };
public List<Item> Items { get; set; } = new List<Item>()
{
new Item { Name = "HH" },
new Item { Name = "ZZ" },
new Item { Name = "MM" }
};
}
public class Item
{
public string Name { get; set; }
}
I am trying to get the values/contents of the "selected" check-boxes so that i can use it to get data from sqlite database in the back-end . But I am unable to get the value of the checkbox from the listview.
This is the listview -
<ListView x:Name="listview" Background="Azure" SelectionMode="Multiple"
ItemsSource="{Binding Strings}" RenderTransformOrigin="0.5,0.5" Width="343">
<ListView.ItemTemplate>
<DataTemplate x:Name="datatemplate">
<CheckBox x:Name="CheckBoxZone" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem},Path=IsSelected}"
Content="{Binding que_text}" Margin="0,5,0,0"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Style="{StaticResource ResourceKey=button_style }" x:Name="addToSurvey" Content="Add" Click="AddToSurvey_Click" />
this is the function-
private void AddToSurvey_Click(object sender, RoutedEventArgs e)
{
//foreach (var item in listview.SelectedItems)
for (int i=0;i< listview.SelectedItems.Count;i++)
{
string que = listview.Items[i].ToString(); //did not work
}
}
this is the Questions class -
public class Questions
{
public int que_id { get; set; }
public string que_text { get; set; }
}
the checkbox hold the que_text value which I need to retrieve for getting the que_id from the database.
In WPF, we use what we call MVVM. Instead of going and poking at the ListViewItem controls like in winforms, we'll put properties for the information we need on our viewmodel classes, and we'll use bindings to tell the UI how to update the viewmodel classes and vice versa.
So we'll add an IsSelected property to Question. We'll also rename that class from Questions to Question, and the collection of questions will now be named Questions instead of Strings.
public class Question
{
public int ID { get; set; }
public string Text { get; set; }
public bool IsSelected { get; set; }
}
Here's your ListView. We bind CheckBox.IsSelected to ListViewItem.IsSelected, so the user can check them just by clicking anywhere on the item. Then we bind Question.IsSelected to ListViewItem.IsSelected in an ItemContainerStyle.
<ListView
x:Name="listview"
Background="Azure"
SelectionMode="Multiple"
ItemsSource="{Binding Questions}"
>
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox
IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem}, Path=IsSelected}"
Content="{Binding Text}" Margin="0,5,0,0"
/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
And here's how we do stuff with the selected questions in that event handler. I'm guessing that your Strings collection was a member of the Window or whatever view you have; let me know if that's not the case and we'll figure out how to get to it. Remember, we're calling that collection Questions now.
private void AddToSurvey_Click(object sender, RoutedEventArgs e)
{
string allQuestionsText = "";
foreach (var question in Questions.Where(q => q.IsSelected))
{
// I don't know what you really want to do in here, but it sounds like you do.
allQuestionsText += question.Text + "\n";
}
}
As far as I know in WPF you can do something like this:
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModels:IronStage1ViewModel}">
<Views:IronStage1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:IronStage2ViewModel}">
<Views:IronStage2View/>
</DataTemplate>
<Views:TestStageToTabIndexConverter x:Key="TestStageToTabIndexConverter" />
</Window.Resources>
My question:
Is there any way to choose the View based on a property in your ViewModel?
something like this:
<Window.Resources> //If property Selector==1
<DataTemplate DataType="{x:Type ViewModels:IronStage1ViewModel}">
<Views:IronStage1View/>
</DataTemplate>
// If property Selector==2
<DataTemplate DataType="{x:Type ViewModels:IronStage1ViewModel}">
<Views:IronStage2View/>
</DataTemplate>
</Window.Resources>
Would a datatemplate selector do this?
tutorial here
This is how this would apply to your scenario:
First create a DataTemplateSelector:
public class IronStageTemplateSelector : DataTemplateSelector
{
public DataTemplate IronStage1Template { get; set; }
public DataTemplate IronStage2Template { get; set; }
public object IronStage1Selector { get; set; }
public object IronStage2Selector { get; set; }
public override DataTemplate SelectTemplate(object selector,
DependencyObject container)
{
if(selector == this.IronStage1Selector)
{
return IronStage1Template;
}
return IronStage2Template;
}
}
I have extended the tutorial to include properties you can assign for when to return each template.
Declare the XAML resources
<UserControl.Resources>
<DataTemplate x:Key="iron1Template">
<TextBlock/>
</DataTemplate>
<DataTemplate x:Key="iron2Template">
<Label />
</DataTemplate>
<System:Double x:Key="Selector1">1</System:Double>
<System:Double x:Key="Selector2">2</System:Double>
<local:IronStageTemplateSelector x:Key="IronStageTemplateSelector"
IronStage1Selector="{StaticResource Selector1}"
IronStage2Selector="{StaticResource Selector2}"
IronStage1Template="{StaticResource iron1Template}"
IronStage2Template="{StaticResource iron2Template}"/>
</UserControl.Resources>
In this example we have declared our selector so that when our property has value 1, template1 is returned, otherwise we get template 2.
Add Control to XAML
Finally, a little hack is needed - your VM property needs to be IEnumerable...
<ItemsControl ItemsSource="{Binding toProperty}"
ItemTemplateSelector="{StaticResource IronStageTemplateSelector}">
</ItemsControl>
I hope this helps, please mark as answer if you found it useful
Is the view model property of known type at compile time? if so you can just add the control directly into main (parent) view and bind datacontext to the view model property.
something like this..
<Address:AddressControl Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="6" DataContext=" {Binding PresentAddress}"/>
Just let me know if you have different scenario.