UWP ListView DataTemplate Bindings - c#

My ViewModel
BookingTakerViewModel
has a property
public List<string> Vias {get;set;}
I am wanting to bind a ListView with a DataTemplate but I can not find out how to do it. This is my code and it is throwing this error when it runs:
"Unable to cast object of type 'System.String' to type 'UI_Test_1.ViewModels.BookingTakerViewModel'."
I thought that the x:DataType should reference the class and in this case it is my Viewmodel.I presume that the x:Bind Vias is wrong beacuase this is a List so lost as to what to do.
<ListView
Name="viasList"
Width="300"
BorderBrush="Black"
BorderThickness="5"
ItemsSource="{x:Bind viewModel.Vias, Mode=OneWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="MinHeight" Value="1" />
<Setter Property="MaxHeight" Value="15" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:Name="dt" x:DataType="viewmodels:BookingTakerViewModel">
<Grid>
<TextBlock FontSize="14" Text="{x:Bind Vias, Mode=OneWay}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The Vias are intantiated with data so it's not that which is causing a NullReference Exception. I believe the XAML is incorrect.

Probably you are looking for something like this?
<ListView.ItemTemplate>
<DataTemplate x:Name="dt" x:DataType="x:String">
<Grid>
<TextBlock FontSize="14" Text="{x:Bind}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Inside the data template, the context of {x:Bind} is the templated object, which is a string item from the collection you set in ItemsSource.

Related

Dynamic Path in Treeview TextBlock

I am trying to change the Path in WPF Treeview TextBlock dynamically via user selection i.e. dropdown. Upon user interaction the path should take predefined values i.e. Name, Type, Order.
<TreeView x:Name="Main" ItemsSource="{Binding Items, NotifyOnSourceUpdated=True}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Root}"
ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Name}" /> <--- Dynamically change this
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
In C# there is an BindingExpression helper class however I am not clear how to use it in ViewModel scenario
If you want to change the actual binding path, you need to do this programmatically. You cannot dynamically change the binding path in XAML.
A better option would be to change the value of the source property based on the selection in the ComboBox, or use a Style with triggers. Something like this:
<HierarchicalDataTemplate DataType="{x:Type models:Root}"
ItemsSource="{Binding Path=Children}">
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue}" Value="Name">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="{Binding Name}" />
</Setter.Value>
</Setter>
</DataTrigger>
...
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</HierarchicalDataTemplate>

ComboBox with fixed Header

I need to show a default text in my ComboBox, this text must not changed also when the user select an item of the Combobox, actually for do this I've created this structure:
<ComboBox ItemsSource="{Binding AvailableNations}" Width="160" Height="55" Margin="0, 0, 0, 15"
Text="Select Countries" IsEditable="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Item.Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
this display as default text Select Countries but if I select an item the default text will disappear and the item selected will be displayed, how can I fix this?
You could use a Combined Template (ref post)
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="NormalItemTemplate" >
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Item.Name}" />
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate" >
<TextBlock>Select Countries</TextBlock>
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding AvailableNations}"
SelectedItem="{Binding SelectedNation}"
ItemTemplate="{StaticResource CombinedTemplate}"
Width="160" Height="55" Margin="0, 0, 0, 15" >
</ComboBox>
</Grid>
The way it works is described in the original answer. Note that the suggested solution will only work when IsEditable is set to false, I assume that won't be a problem in your case. Second, to get the text displayed at start up I bound SelectedItem (e.g. to the first item in the collection).
From the comments, it sounds like you just want the Select Countries text to display at all times, even when an item is selected.
Personally I would just go the simple route and place a TextBox on top of the ComboBox, and hide the Display Text of the ComboBox by using a Transparent Foreground color.
Here's a quick example demonstrating it :
<Grid>
<ComboBox SelectedIndex="1" Foreground="Transparent">
<ComboBox.Resources>
<Style TargetType="{x:Type ComboBoxItem}">
<!-- Make sure ComboBoxItems don't have transparent text -->
<Setter Property="Foreground" Value="{StaticResource {x:Static SystemColors.ControlTextBrushKey}}" />
</Style>
</ComboBox.Resources>
<ComboBoxItem>Test 1</ComboBoxItem>
<ComboBoxItem>Test 2</ComboBoxItem>
<ComboBoxItem>Test 3</ComboBoxItem>
</ComboBox>
<TextBlock Text="Select Countries" Margin="4,3" IsHitTestVisible="False" />
</Grid>
And the results (note that SelectedIndex = 1)
I'm sure there's other ways too, such as overwriting the way it paints the display text, or changing the control template, but this seemed like the easiest to me.

Updating TabControl ItemSource ViewModels when specific state changes

I have a TabControl bound to an ObservableCollection property of view models, TabViewModelsCollection.
When another property gets set from within the view models, DeviceState, I'd like to raise a property changed event and tell my TabControls ItemSource to refresh.
The problem is my ItemSource is an ObservableCollection of ViewModels and when I call RaisePropertyChanged("TabViewModelsCollection"); nothing gets updated.
Furthermore my tab views contain multiple user controls and bindings.
The scenario that is supposed to play out is: A device is located on the network, data is collected, and the tabs of device information should then be updated.
Currently my TabControl only updates when I select a different device then select the device whos information I want to see. Think left panel list of devices, right panel with tabs of device info.
Let me know what part of the code you guys might want to see, my codebase is quite large so it would be hard to post it.
Here is where the TabControl is defined in my view:
<!-- Devist List Controls -->
<Grid DockPanel.Dock="Left" Margin="5,5">
<local:DeviceListView DataContext="{Binding DeviceListViewModel}" Grid.Row="0"/>
</Grid>
<GroupBox Header="Device Information" DockPanel.Dock="Right" Margin="0,0,5,5">
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection}" SelectedItem="{Binding DeviceListViewModel.SelectedDevice.SelectedTabItemVm}" >
<TabControl.Resources>
<DataTemplate DataType="{x:Type vms:HomeViewModel}">
<local:HomeTab/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ConfigurationViewModel}">
<Grid>
<local:ConfigurationFileView Visibility="{Binding Configuration, TargetNullValue=Collapsed, FallbackValue=Visible}"/>
<local:ErrorTab Visibility="{Binding Path= Configuration, TargetNullValue=Visible, FallbackValue=Hidden}"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ExpansionModulesViewModelFactory}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<DockPanel >
<local:ExpansionModulesList Title="Discovered/Enumerated"
DataContext="{Binding DiscoveredModules}"
/>
<GridSplitter Width="5"/>
<local:ExpansionModulesList Title="User Action Required"
DataContext="{Binding FaultyModules}"
/>
</DockPanel>
</StackPanel>
<DockPanel Grid.Row="1">
<StackPanel Orientation="Horizontal" FlowDirection="RightToLeft" Margin="5" IsEnabled="{Binding IsCommandEnabled}">
<Button Content="Cancel" HorizontalAlignment="Right"
Command="{Binding CancelExpansionCommand }"
ToolTip="Revert all local modifications by refreshing data from the controller." />
<Separator Width="10"/>
<Button Content="Apply" HorizontalAlignment="Center"
Command="{Binding ApplyExpansionCommand }"
ToolTip="Apply all changes to the controller." />
<Separator/>
</StackPanel>
</DockPanel>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:LogViewModel}">
<local:LogView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:SignalStrengthViewModel}">
<local:SignalStrengthView />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</TabControl.ItemContainerStyle>
EDIT: I want to be able to call raised property changed on this and have it refresh all of my tab views..
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection}" SelectedItem="{Binding DeviceListViewModel.SelectedDevice.SelectedTabItemVm}" >
RaisePropertyChanged("TabViewModelsCollection");
Hi please try the next solution:
VM code redaction
private State _deviceState;
private ObservableCollection<object> _tabViewModelsCollection;
public State DeviceState
{
get { return _deviceState; }
set
{
_deviceState = value;
RaisePropertyChanged("DeviceState");
UpdateTabViewModelsCollection();
}
}
public ObservableCollection<object> TabViewModelsCollection
{
get
{
return _tabViewModelsCollection ??
(_tabViewModelsCollection = new ObservableCollection<object>(GetDeviceData()));
}
}
private void UpdateTabViewModelsCollection()
{
_tabViewModelsCollection = null;
RaisePropertyChanged("TabViewModelsCollection");
}
private List<object> GetDeviceData()
{
//implement here the data collection process
throw new NotImplementedException();
}
Xaml redaction (define the UpdateSourceTrigger)
ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection, UpdateSourceTrigger=PropertyChanged}"
Let me know if it was helpful.
Regards.

How to access the nested resource in WPF?

I have the following resource:
<Window.Resources>
<Style x:Key="TopKey" TargetType="local:CustomType">
<Style.Resources>
<DataTemplate x:Key="NestedKey">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</Style.Resources>
</Style>
</Window.Resources>
Then I have the following declaration:
<local:CustomType ItemTemplate="{StaticResource TopKey.NestedKey}"/>
Of course the above line doesn't compile and I don't know how to resolve this...
Putting a Resource in a ResourceDictionary of a FrameworkElement means that you don't want the Resource to be accessible outside this FrameworkElement (though you could get around it in code behind).
In your case NestedKey is in the wrong ResourceDictionary. Try something like this:
<Window.Resources>
<DataTemplate x:Key="NestedKey">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
<Style x:Key="TopKey" TargetType="local:CustomType">
<!-- here I can use {StaticResource NestedKey} -->
</Style>
</Window.Resources>
<!-- in the same window I can use: -->
<local:CustomType ItemTemplate="{StaticResource NestedKey}"/>
You could also define a new Style that is based on the TopKey Resource, thus gaining access to it's ResourceDictionary (but that is a workaround to something you can do better)
<local:CustomType>
<local:CustomType.Style>
<Style BasedOn={StaticResource TopKey} TargetType="local:CustomType">
<!-- here I can use {StaticResource NestedKey} -->
</Style>
</local:CustomType.Style>
</local:CustomType>
simply do this
<Window.Resources>
<DataTemplate x:Key="NestedKey">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
<Style x:Key="TopKey" TargetType="local:CustomType">
<Setter Property="ItemTemplate" Value="{StaticResource NestedKey}" />
</Style>
</Window.Resources>
<local:CustomType Style="{StaticResource TopKey}" />
hope that helps

wpf combobox with custom itemtemplate text

I have ComboBox with custom ItemTemplate.
<ComboBox Height="20" Width="200"
SelectedItem="{Binding Path=SelectedDesign}"
ItemsSource="{Binding Path=Designs}" HorizontalAlignment="Left"
ScrollViewer.CanContentScroll="False">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type formdesign:FormDesignContainer}">
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding Path=ImageThumb}" Stretch="Uniform" />
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This works well. However WPF tries to draw rectangle as Combobox Text. How can I set "text" for this template. By "text" I mean string or control which represent selected item and write into combobox when item is selected
In other words I'd like to do this:
But now I got this
Try setting SelectionBoxItemTemplate with a TextBlock.
Appears that SelectionBoxItemTemplate is read-only. So another approach is to override ItemContainerStyle.Template. Example
I found this solution by Ray Burns a good approach. You can define two DataTemplate one for Items in the drop down list and the other for the selected item which should be shown in the Combobox. The using a trigger and checking the visual tree it decide which one to use.
<Window.Resources>
<DataTemplate x:Key="NormalItemTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
...
<ComboBox
ItemTemplate="{StaticResource CombinedTemplate}"
ItemsSource="..."/>
Add Textblock to the datatemplate and bind it
or add Contentpersenter on the rectangle
Edit:
it seems like i didn't got what you were tring to accomplish ,

Categories

Resources