Two ListBoxes with two (almost similar) templates - wpf
I have 2 Listboxes with 2 data templates that are almost identical except that one of them contains TextBox instead of ComboBox.
First Template :
<DataTemplate x:Key="OldPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
<Grid Height="60" Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue"
Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex),
Converter={StaticResource IncrementerConverter}}" />
</Border>
<TextBlock Grid.Column="1" Text="{Binding Name}" />
<TextBlock Grid.Column="2" Text="{Binding DistributionSystemName}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
</Grid>
</DataTemplate>
Second Template :
<DataTemplate x:Key="NewPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
<Grid Height="60">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue"
Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex),
Converter={StaticResource IncrementerConverter}}" />
</Border>
<TextBlock Grid.Column="1" Text="{Binding Name}" />
<ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}"
SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"
IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}"
ItemTemplate="{StaticResource DistributionSystemTemplate}" >
</ComboBox>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of available ways: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfAvailableWays}" />
</Grid>
</DataTemplate>
As you can see they are both almost identical except for this part :
<ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}"
SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"
IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}"
ItemTemplate="{StaticResource DistributionSystemTemplate}" >
</ComboBox>
The problem is whenever i change anything in one of them i have to change the same thing in the other also ... Any way that can merge them in some way and make the combobox the only variable that changes according to which listbox is calling the template ?
Here an attempt on how you can achieve that, but I have to admit that it is messy but answers your question blindly! a better approach would be to properly implement a DataTemplateSelector.
The idea is to decouple the changing parts into two separate DataTemplates and put them into the resources, one with the Combobox and one for the TextBlock in your case:
<DataTemplate x:Key="DataTemplateCombobox">
<ComboBox ItemsSource="{Binding ValidDistributionSystemsForPanel}" ...>
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTextblock" >
<TextBlock Text="{Binding DistributionSystemName}" ... />
</DataTemplate>
Now those controls will be replaced with a ContentPresenter in your main (common) DataTemplate. The ContentPresenter uses a ContentTemplateSelector to select which sub-DataTemplate to used based on the name of the ListBox on which this DataTemplate is applied, this is why the Content is bound directly o the ListBox using ancestry binding:
<local:ValueDataTemplateSelector x:Key="TemplateSelector"
DefaultDataTemplate="{StaticResource DataTemplateTextblock}"
ComboboxDataTemplate="{StaticResource DataTemplateCombobox}"
TextBlockDataTemplate="{StaticResource DataTemplateTextblock}" />
<DataTemplate x:Key="OldPanelsTemplate">
<Grid Height="60" Margin="0" Name="OldPanelsTemplateGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" Text="tex" />
</Border>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
<ContentPresenter ContentTemplateSelector="{StaticResource TemplateSelector}" Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Grid.Row="0" Grid.Column="2">
</ContentPresenter>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
</Grid>
</DataTemplate>
And here how your DataTemplateSelector should be implemented (basically):
public class ValueDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultDataTemplate { get; set; }
public DataTemplate ComboboxDataTemplate { get; set; }
public DataTemplate TextBlockDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var lb = item as ListBox;
if (lb is null)
return DefaultDataTemplate;
if (lb.Name == "ListOne")
return ComboboxDataTemplate;
if (lb.Name == "ListTwo")
return TextBlockDataTemplate;
return DefaultDataTemplate;
}
}
Finally, since your sub-DataTemplates lose their DataContexts due to the Content of the ContentPresenter being bound to the ListBox directly, then just hook their DataContexts again using ElementName Binding or something:
<DataTemplate x:Key="DataTemplateCombobox">
<ComboBox DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}" ItemsSource="{Binding ValidDistributionSystemsForPanel}" >
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTextblock" >
<TextBlock Text="{Binding DistributionSystemName}" DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"/>
</DataTemplate>
OldPanelsTemplateGrid is the First Grid in the main DataTemplate that should have the valid ListBoxItem DataContext.
Here is the full Xaml code:
</Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800" >
<Window.Resources>
<DataTemplate x:Key="DataTemplateCombobox">
<ComboBox DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}" ItemsSource="{Binding ValidDistributionSystemsForPanel}" >
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTextblock" >
<TextBlock Text="{Binding DistributionSystemName}" DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"/>
</DataTemplate>
<local:ValueDataTemplateSelector x:Key="TemplateSelector"
DefaultDataTemplate="{StaticResource DataTemplateTextblock}"
ComboboxDataTemplate="{StaticResource DataTemplateCombobox}"
TextBlockDataTemplate="{StaticResource DataTemplateTextblock}" />
<DataTemplate x:Key="OldPanelsTemplate">
<Grid Height="60" Margin="0" Name="OldPanelsTemplateGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" Text="tex" />
</Border>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
<ContentPresenter ContentTemplateSelector="{StaticResource TemplateSelector}" Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Grid.Row="0" Grid.Column="2">
</ContentPresenter>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
</Grid>
</DataTemplate>
</Window.Resources>
<!--DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorLevel=2,AncestorType=DataTemplate}}"-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox x:Name ="ListOne" ItemsSource="{Binding MyCollection}" ItemTemplate="{StaticResource OldPanelsTemplate}"/>
<ListBox x:Name ="LisTwo" ItemsSource="{Binding MyCollection}" ItemTemplate="{StaticResource OldPanelsTemplate}" Grid.Row="1"/>
</Grid>
I managed to solve it by merging them in one template like this:
<DataTemplate x:Key="NewOldPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
<Grid Height="60">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue"
Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex),
Converter={StaticResource IncrementerConverter}}" />
</Border>
<TextBlock Grid.Column="1" Text="{Binding Name}" />
<!-- To have same template for new and old panels we had the two elements (combobox and textblock) for distribution system and toggle visibility by converters according to their groupbox title -->
<ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}"
SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"
IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}"
ItemTemplate="{StaticResource DistributionSystemTemplate}"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=GroupBox, Mode=FindAncestor}, Path=Header,Converter={StaticResource NewPanelsTemplateVisibilityConverter}}">
</ComboBox>
<TextBlock Grid.Column="2" Text="{Binding DistributionSystemName}"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=GroupBox, Mode=FindAncestor}, Path=Header,Converter={StaticResource OldPanelsTemplateVisibilityConverter}}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of available ways: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfAvailableWays}" />
</Grid>
</DataTemplate>
and control the visibility of variable parts(ComboBox and TextBlock in my case) with converters like this :
public class OldPanelsTemplateVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((string)value == "Old Panels")
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
}
public class NewPanelsTemplateVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((string)value == "Old Panels")
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}}
The solution above is based on that each of my listboxes is surrounded by groupbox with certain header (In other cases you may want to use ListBox Names in the converter as visibility toggle)
Thanks to #PavelAnikhouski for the idea of converters.
Also i tried the solution of DataTemplateSelector proposed by #SamTheDev and it worked also (Thanks #SamTheDev), but i preferred the converter solution because i didn't feel comfortable with the idea of losing datacontext through using content presenter (The bottom line here is both solutions work and no one is more elegant it is just personal preference)
Related
listboxitem with listbox recursive
I have a listboxitemtemplate that contains controls in a grid and a list box. That listbox's listboxitemtemplate contains the same controls in the grid prior. I am looking for a way to datatemplate the listboxitemtemplate so that datatemplate can be used as the contenttemplate in both of the two listboxes. But I am running into dead end because the second listbox is located in the first listbox's listitemtemplate. Below is the code. If any one has any ideas how so I can where I could come up with a scheme to datatemplate the first listbox's listboxitemtemplate minus the second listbox, and use the same datatemple in the second listbox. <ListBox x:Name="lbLista" ItemsSource="{Binding ListBoxCollection}" > <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <dxe:TextEdit Grid.Column="0" Text="{Binding Section}" TextWrapping="NoWrap" /> <dxe:ToggleSwitch Grid.Column="1" /> <TextBlock TextTrimming="CharacterEllipsis" Grid.Column="2" Text ="{Binding SectionText}" /> <dxe:TextEdit Grid.Column="3" Text ="{Binding SectionStatus}" /> <dx:DXExpander Grid.Row="1" Grid.Column="0"> <DockPanel HorizontalAlignment="Stretch" Background="Purple"> <dxre:RichEditControl DockPanel.Dock="Top" > <dxre:RichEditControl.HorizontalRulerOptions> <dxre:DXRichEditHorizontalRulerOptions Visibility="Hidden"/> </dxre:RichEditControl.HorizontalRulerOptions> <dxre:RichEditControl.VerticalRulerOptions> <dxre:DXRichEditVerticalRulerOptions Visibility="Hidden"/> </dxre:RichEditControl.VerticalRulerOptions> </dxre:RichEditControl> <ListBox x:Name="lbListb" ItemsSource="{Binding ListBoxCollectionb}" > <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <dxe:TextEdit Grid.Column="0" Text="{Binding Section}" TextWrapping="NoWrap" /> <dxe:ToggleSwitch Grid.Column="1" /> <TextBlock TextTrimming="CharacterEllipsis" Grid.Column="2" Text ="{Binding SectionText}" /> <dxe:TextEdit Grid.Column="3" Text ="{Binding SectionStatus}" /> <dx:DXExpander Grid.Row="1" Grid.Column="0"> <DockPanel HorizontalAlignment="Stretch" > <dxre:RichEditControl DockPanel.Dock="Top" > <dxre:RichEditControl.HorizontalRulerOptions> <dxre:DXRichEditHorizontalRulerOptions Visibility="Hidden"/> </dxre:RichEditControl.HorizontalRulerOptions> <dxre:RichEditControl.VerticalRulerOptions> <dxre:DXRichEditVerticalRulerOptions Visibility="Hidden"/> </dxre:RichEditControl.VerticalRulerOptions> </dxre:RichEditControl> </DockPanel> </dx:DXExpander> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </DockPanel> </dx:DXExpander> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
WPF MVVM Combobox SelectedItem not working properly
I've already searched and read from several posts but I couldn't find what I am doing wrong. I have a ComboBox with an ObservableCollection<string> Available_COMPorts as ItemsSource. On SelectedValue I binded a string named SelectedCOMPort with Mode = TwoWay. <Label Content="Porta COM: "></Label> <ComboBox ItemsSource="{Binding Available_COMPorts}" SelectedValue="{Binding SelectedCOMPort, Mode=TwoWay}" /> After bofere the combobox, I have a Label displaying the SelectedCOMPort <Label Content="Status: " /> <Label Content="{Binding SelectedCOMPort}" Foreground="Red" /> I have made both Available_COMPorts and SelectedCOMPort with INotifyPropertyChanged. On my ViewModel Initialization, I filled the Available_COMPorts with the three SerialPort strings available ("COM6", "COM5", "COM4") and set SelectedCOMPort = "Available_COMPorts[0]" ("COM6"). When I Run the code, ComboBox has the three itens, the selected item is "COM6" and the label shows "COM6" (everything is fine). Then I select "COM5" and the label updates it's value to "COM5". This is presented on the following pictures. The problem is when I try to access SelectedCOMPort on the ViewModel, as I need the selected item to connect on my SerialPort. The SelectedCOMPort is always as the default value "COM6". I try to access the SelectedCOMPorton the connect click command. Debbuging, using Breakpoints on INotifyProperty functions I realized that the binding property seems to be working fine, but when it leaves INotifyProperty the value goes back to default. Why is that happening? I've tryed several approachs and none worked for me. Following is my ViewModel and BaseViewModel with INotifyProperty: BaseViewModel : INotifyPropertyChanged public class BaseViewModel : INotifyPropertyChanged { #region PropertyChange public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged([CallerMemberName] string propertyName = "") { if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } protected void SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(backingField, value)) return; backingField = value; OnPropertyChanged(propertyName); } #endregion } ViewModel public class LiveGraphViewModel : BaseViewModel { public ConnectButtonCommand ConnectButtonCommand { get; set; } private ObservableCollection<string> _availableCOMPorts; private string _selectedCOMPort; public ObservableCollection<string> Available_COMPorts { get { return _availableCOMPorts; } set { SetProperty(ref _availableCOMPorts, value); } } public string SelectedCOMPort { get { return _selectedCOMPort; } set { SetProperty(ref _selectedCOMPort, value); } } public LiveGraphViewModel() { this.ConnectButtonCommand = new ConnectButtonCommand(this); ObservableCollection<string> TempCOM = new ObservableCollection<string>(); foreach (string comport in SerialPort.GetPortNames()) TempCOM.Add(comport); Available_COMPorts = TempCOM; if(Available_COMPorts.Count > 0) SelectedCOMPort = Available_COMPorts[0]; } public void ConnectButton() { if (SelectedCOMPort == "COM5") Connect(SelectedCOMPort); } } LiveGraphView XAML <UserControl x:Class="SmartAthleticsWPF.Views.LiveGraphView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:SmartAthleticsWPF.Views" xmlns:viewmodels="clr-namespace:SmartAthleticsWPF.ViewModels" xmlns:commands="clr-namespace:SmartAthleticsWPF.Commands" xmlns:syncfusion="clr-namespace:Syncfusion.UI.Xaml.Charts;assembly=Syncfusion.SfChart.WPF" xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <UserControl.Resources> <viewmodels:LiveGraphViewModel x:Key="LIVviewModel"/> </UserControl.Resources> <Grid Background="#EDEDED"> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.01*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="0.5*"/> <ColumnDefinition Width="0.01*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="0.1*"/> <RowDefinition Height="1.2*"/> <RowDefinition Height="2*"/> <RowDefinition Height="*"/> <RowDefinition Height="0.1*"/> </Grid.RowDefinitions> <!--LIVE GRAPH BORDER--> <Border Grid.Row="1" Grid.RowSpan="2" Grid.Column="1" Grid.ColumnSpan="2" Margin="5" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Padding="10,10,30,10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <DockPanel> <Label Content="Live Graph" DockPanel.Dock="Top" HorizontalAlignment="Center" Padding="0,5,0,10" FontSize="22"/> <syncfusion:SfChart x:Name="LiveGraphChart" > <syncfusion:SfChart.PrimaryAxis> <syncfusion:NumericalAxis Header="Seconds" Maximum="{Binding MaxXAxis}" Minimum="{Binding MinXAxis}"/> </syncfusion:SfChart.PrimaryAxis> <syncfusion:SfChart.SecondaryAxis> <syncfusion:NumericalAxis Header="Kgf"/> </syncfusion:SfChart.SecondaryAxis> <syncfusion:SfChart.Series> <syncfusion:FastLineBitmapSeries ItemsSource="{Binding FyCircularBuffer}" XBindingPath="XData" YBindingPath="YData" StrokeThickness="1" Interior="DarkGreen" ShowTooltip="False" ShowTrackballInfo="False" IsSeriesVisible="{Binding FyChecked}"/> <syncfusion:FastLineBitmapSeries ItemsSource="{Binding MyCircularBuffer}" XBindingPath="XData" YBindingPath="YData" StrokeThickness="1" Interior="LimeGreen" ShowTooltip="False" ShowTrackballInfo="False" IsSeriesVisible="{Binding MyChecked}"/> <syncfusion:FastLineBitmapSeries ItemsSource="{Binding FxCircularBuffer}" XBindingPath="XData" YBindingPath="YData" StrokeThickness="1" Interior="IndianRed" ShowTooltip="False" ShowTrackballInfo="False" IsSeriesVisible="{Binding FxChecked}"/> <syncfusion:FastLineBitmapSeries ItemsSource="{Binding MxCircularBuffer}" XBindingPath="XData" YBindingPath="YData" StrokeThickness="1" Interior="Red" ShowTooltip="False" ShowTrackballInfo="False" IsSeriesVisible="{Binding MxChecked}"/> <syncfusion:FastLineBitmapSeries ItemsSource="{Binding FzCircularBuffer}" XBindingPath="XData" YBindingPath="YData" StrokeThickness="1" Interior="BlueViolet" ShowTooltip="False" ShowTrackballInfo="False" IsSeriesVisible="{Binding FzChecked}"/> <syncfusion:FastLineBitmapSeries ItemsSource="{Binding MzCircularBuffer}" XBindingPath="XData" YBindingPath="YData" StrokeThickness="1" Interior="Blue" ShowTooltip="False" ShowTrackballInfo="False" IsSeriesVisible="{Binding MzChecked}"/> </syncfusion:SfChart.Series> </syncfusion:SfChart> </DockPanel> </Border> <!--COP BORDER--> <Border Grid.Row="2" Grid.Column="3" Margin="5" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Padding="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <DockPanel> <Label Content="C O P" Grid.Column="0" Grid.ColumnSpan="2" DockPanel.Dock="Top" HorizontalAlignment="Center" FontSize="16" FontWeight="Bold" Padding="0,0,0,5"/> <syncfusion:SfChart x:Name="CopChart" VerticalContentAlignment="Stretch" HorizontalAlignment="Center" AreaBorderThickness="5" AreaBorderBrush="#523B97"> <syncfusion:SfChart.PrimaryAxis> <syncfusion:NumericalAxis Minimum="-20" Maximum="20" Interval="10" PlotOffset="5" BorderThickness="0" TickLineSize="0" FontSize="12"> <syncfusion:NumericalAxis.AxisLineStyle> <Style TargetType="Line"> <Setter Property="StrokeThickness" Value="0"/> </Style> </syncfusion:NumericalAxis.AxisLineStyle> </syncfusion:NumericalAxis> </syncfusion:SfChart.PrimaryAxis> <syncfusion:SfChart.SecondaryAxis> <syncfusion:NumericalAxis Minimum="-30" Maximum="30" Interval="10" PlotOffset="5" BorderThickness="0" TickLineSize="0" FontSize="12"> <syncfusion:NumericalAxis.AxisLineStyle> <Style TargetType="Line"> <Setter Property="StrokeThickness" Value="0"/> </Style> </syncfusion:NumericalAxis.AxisLineStyle> </syncfusion:NumericalAxis> </syncfusion:SfChart.SecondaryAxis> <syncfusion:SfChart.Annotations> <syncfusion:LineAnnotation X1="00" X2="00" Y1="-30" Y2="30" Stroke="DimGray" StrokeThickness="2" StrokeDashArray="4"/> <syncfusion:LineAnnotation X1="-20" X2="20" Y1="00" Y2="0" Stroke="DimGray" StrokeThickness="2" StrokeDashArray="4"/> </syncfusion:SfChart.Annotations> <syncfusion:SfChart.Series> <syncfusion:FastScatterBitmapSeries ItemsSource="{Binding COP_DOT}" XBindingPath="XData" YBindingPath="YData" Interior="Red" ScatterHeight="20" ScatterWidth="20"/> </syncfusion:SfChart.Series> </syncfusion:SfChart> </DockPanel> </Border> <!--SERIAL BORDER--> <Border Grid.Row="1" Grid.Column="3" Margin="5" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Padding="15,0,15,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Label Content="Conexão" Grid.Column="0" Grid.ColumnSpan="2" DockPanel.Dock="Top" HorizontalAlignment="Center" FontSize="16" FontWeight="Bold" Padding="0,0,0,5"/> <DockPanel Grid.Row="1" Grid.ColumnSpan="2" LastChildFill="True"> <Label Content="Porta COM: "></Label> <ComboBox ItemsSource="{Binding Available_COMPorts}" SelectedValue="{Binding SelectedCOMPort, Mode=TwoWay}" Margin="0,0,0,15" /> <!--SelectedItem="{Binding SelectedCOMPort, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}"--> </DockPanel> <Button Grid.Row="2" Grid.ColumnSpan="2" Content="Connect/Disconnect" HorizontalAlignment="Stretch" Margin="5" Command="{Binding Path=ConnectButtonCommand, Source={StaticResource LIVviewModel}}"/> <StackPanel Orientation="Horizontal" Grid.Row="3" Grid.ColumnSpan="2"> <Label Content="Status: "></Label> <Label Content="{Binding SelectedCOMPort}" Foreground="Red" /> </StackPanel> </Grid> </Border> <Grid Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <!--SERIAL BORDER--> <Border Grid.Column="0" Margin="5" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Padding="20,0,20,0"> </Border> <!--RECORD BORDER--> <Border Grid.Column="1" Margin="5" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Padding="20,0,20,0"> <Grid Margin="5,10,5,10" VerticalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="2*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="1.5*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Button Grid.Row="0" Grid.ColumnSpan="2" Margin="0,0,0,10" Content="Click me" Command="{Binding Path=RecordButtonCommand, Source={StaticResource LIVviewModel}}" /> <Label Content="Record Time (s):" Grid.Column="0" Grid.Row="1" VerticalAlignment="Center"/> <TextBlock Text="30" Background="White" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center"/> <Label Content="Name:" Grid.Column="0" Grid.Row="2" VerticalAlignment="Center"/> <TextBlock Text="Subject Name" Grid.Column="1" Grid.Row="2" VerticalAlignment="Center"/> </Grid> </Border> <!--Informações do Gráfico BORDER--> <Border Grid.Column="2" Margin="5" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Padding="10,0,10,0"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Label Content="Informações do Gráfico" Grid.Column="0" Grid.ColumnSpan="6" HorizontalAlignment="Center" FontSize="16" FontWeight="Bold" /> <CheckBox Content="Fy" Grid.Column="0" Grid.Row="1" FontWeight="ExtraBlack" FontSize="12" Foreground="DarkGreen" HorizontalAlignment="Center" IsChecked="{Binding FyChecked}" /> <CheckBox Content="My" Grid.Column="1" Grid.Row="1" FontWeight="ExtraBlack" FontSize="12" Foreground="LimeGreen" HorizontalAlignment="Center" IsChecked="{Binding MyChecked}" /> <CheckBox Content="Fx" Grid.Column="2" Grid.Row="1" FontWeight="ExtraBlack" FontSize="12" Foreground="IndianRed" HorizontalAlignment="Center" IsChecked="{Binding FxChecked}" /> <CheckBox Content="Mx" Grid.Column="3" Grid.Row="1" FontWeight="ExtraBlack" FontSize="12" Foreground="Red" HorizontalAlignment="Center" IsChecked="{Binding MxChecked}" /> <CheckBox Content="Fz" Grid.Column="4" Grid.Row="1" FontWeight="ExtraBlack" FontSize="12" Foreground="BlueViolet" HorizontalAlignment="Center" IsChecked="{Binding FzChecked}" /> <CheckBox Content="Mz" Grid.Column="5" Grid.Row="1" FontWeight="ExtraBlack" FontSize="12" Foreground="Blue" HorizontalAlignment="Center" IsChecked="{Binding MzChecked}" /> <DockPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" LastChildFill="True" HorizontalAlignment="Stretch"> <Label Content="Periodo: " VerticalAlignment="Center"/> <ComboBox Text="30" Background="White" VerticalAlignment="Center" SelectedIndex="{Binding PeriodSelectedIndex}"> <!--5--> <ComboBoxItem Content="100 ms" /> <ComboBoxItem Content="500 ms" /> <ComboBoxItem Content="1 s" /> <ComboBoxItem Content="5 s" /> <ComboBoxItem Content="10 s" /> <ComboBoxItem Content="30 s" /> </ComboBox> </DockPanel> <DockPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" LastChildFill="True" HorizontalAlignment="Stretch"> <Label Content="Amplitude: " VerticalAlignment="Center"/> <ComboBox Text="30" Background="White" VerticalAlignment="Center" SelectedIndex="{Binding AmplitudeSelectedIndex}"> <!--3--> <ComboBoxItem Content="10"/> <ComboBoxItem Content="100"/> <ComboBoxItem Content="500"/> <ComboBoxItem Content="1000"/> <ComboBoxItem Content="5000"/> <ComboBoxItem Content="10000"/> </ComboBox> </DockPanel> <Label Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="3" Content="{Binding PesoKg}" ContentStringFormat="Peso (kg): {0}" VerticalAlignment="Center" HorizontalAlignment="Center"/> <Label Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="3" Content="{Binding PesoNw}" ContentStringFormat="Força (N): {0}" VerticalAlignment="Center" HorizontalAlignment="Center"/> </Grid> </Border> </Grid> </Grid> </UserControl> Connect Button public class ConnectButtonCommand : ICommand { public LiveGraphViewModel ViewModel { get; set; } public ConnectButtonCommand(LiveGraphViewModel viewModel) { this.ViewModel = viewModel; } public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { this.ViewModel.ConnectButton(); } } DataContext public partial class LiveGraphView : UserControl { private LiveGraphViewModel _vm; public LiveGraphView() { InitializeComponent(); this._vm = new LiveGraphViewModel(); this.DataContext = this._vm; } }
As mentioned by #Clemens I had two view model instances at the same time. One created in the UserControl's constructor (code behind) and other on the XAML Resources. So, I removed the last one and everything works fine. <!-- <UserControl.Resources> <viewmodels:LiveGraphViewModel x:Key="LIVviewModel"/> </UserControl.Resources> --> <Label Content="Porta COM: "></Label> <ComboBox ItemsSource="{Binding Available_COMPorts}" SelectedValue="{Binding SelectedCOMPort, Mode=TwoWay}" /> <Button Content="Connect/Disconnect" Command="{Binding Path=ConnectButtonCommand}" />
TextBlock TextWrapping not wrapping #2
OK... so this solution doesn't help XAML is here <ListBox ItemsSource="{Binding Path=ContentItems}" Background="White" > <ListBox.ItemTemplate> <DataTemplate> <Grid Margin="2" Height="Auto" Width="Auto"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid Grid.Column="0"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal"> <Label VerticalAlignment="Center" Margin="0,0,0,5">Start</Label><svl:TimeEditor Value="{Binding Path=FormatedStart}" Width="87" HorizontalAlignment="Left" Margin="2,8" Name="dtpStart" FontSize="12" Height="25" VerticalAlignment="Center" /> <Label VerticalAlignment="Center" Margin="0,0,0,5">End</Label><svl:TimeEditor Value="{Binding Path=FormatedEnd}" Width="87" HorizontalAlignment="Left" Margin="2,8" Name="dtpEnd" FontSize="12" Height="25" VerticalAlignment="Center" /> </StackPanel> <TextBlock Grid.Row="1" TextWrapping="Wrap" Name="tbText" Text="{Binding Path=Data}"></TextBlock> </Grid> <Grid Grid.Column="1" Visibility="Collapsed"> </Grid> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
The following will helps for text wrapping: <ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
Well your TextBlock does not need to wrap since your specifying Width as Auto for it's ColumnDefinition which allows it to take all the Width it needs to fit Content even at the cost of overflowing. You either need to set the Column's Width to "*" to allow the TextWrapping to kick in when requested width exceeds allowable or manually force a MaxWidth on it using a Binding like <TextBlock Name="tbText" Grid.Row="1" MaxWidth="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualWidth}" Text="{Binding Path=Data}" TextWrapping="Wrap" />
Binding to property of DataTemplate of DataType
How can I bind to property of view in DataTemplate? Here is my code. You can see my attempt in MainWindow.xaml (Grid column 2, row 0) to bind to selected item in TreeView from column 0 - row - 0, but this does not work. If I move TreeView to MainWindow.xaml instead of using DataTemplate - everything works as expected. NOTE: Repository collection ({Binding Repositories} in MainWindow.xaml in column 0, row 0) is collection of RepositoryVM instances and its Items propery contains collection of ItemVM instances. Also, Children propery of ItemVM contains collections of ItemVM insances. Code: MainWindow.xaml <Grid Margin="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="4" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Border Grid.Column="0" Grid.Row="0" MinWidth="200" > <ContentControl Content="{Binding Repositories}" ContentTemplate="{StaticResource WorkspaceTemplate}"/> </Border> <GridSplitter Grid.Column="1" ResizeDirection="Columns" ResizeBehavior="PreviousAndNext" Width="4" Height="Auto"/> <Border Grid.Column="2" Grid.Row="0"> <ContentControl Content="{Binding ElementName=repoTree, Path=SelectedItem}"/> </Border> <StatusBar Grid.ColumnSpan="3" Grid.Column="0" Grid.Row="1"> <TextBlock Text="{Binding ElementName=repoTree, Path=SelectedItem}" /> </StatusBar> </Grid> MainWindowResources.xaml <DataTemplate DataType="{x:Type vm:ItemVM}"> <vw:ItemView /> </DataTemplate> <DataTemplate DataType="{x:Type vm:RepositoryVM}"> <vw:RepositoryView /> </DataTemplate> <DataTemplate x:Key="WorkspaceTemplate"> <TabControl ItemsSource="{Binding}"> <TabControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding DisplayName}" /> </DataTemplate> </TabControl.ItemTemplate> </TabControl> </DataTemplate> RepositoryView.xaml <TreeView ItemsSource="{Binding Items}" x:Name="repoTree"> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Name}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> ItemView.xaml <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <TextBlock Grid.Column="0" Grid.Row="0" Text="Name: " /> <TextBlock Grid.Column="0" Grid.Row="1" Text="Type:" /> <TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Name}"/> <TextBox Grid.Column="1" Grid.Row="1" /> </Grid> Question is - how can I get my attempt of binding to repoTree to work and still use DataTemplate? Update: Error I get in output window is: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=repoTree'. BindingExpression:Path=SelectedItem; DataItem=null; target element is 'ContentControl' (Name=''); target property is 'Content' (type 'Object') And the same one for statusbar, because it is the same binding.
WPF ListBox: how to update data with binding
I have listbox that displays information about list of objects: <ListBox Grid.Column="0" Height="152" Name="CustomersList" HorizontalAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name, Mode=OneWay}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> I would show more detailed information about selected item in another block, but I don't know (and can't find) how to bind selected item to those block data context. I guess it should be something like this: <Grid Grid.Column="1" DataContext="{Binding Path=ItemSelected, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top"> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition Width="250"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="Id" VerticalAlignment="Center" /> <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Id}" VerticalAlignment="Center"/> <TextBlock Grid.Row="1" Grid.Column="0" Text="Name" VerticalAlignment="Center"/> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Name}" VerticalAlignment="Center"/> <StackPanel Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center"> <Button Content="Add new" /> <Button Content="Store changes" /> </StackPanel> </Grid> But the problem is that data item to be binded to the grid is not specified anywhere and I don't know how to do that. Any suggestion would be wellcome. Thanks! P.S. I've tried to specify CustomersList.ItemsSource as DataContext for the Grid - this didn't give any result.
You can bind to the SelectedItem property of the ListBox, one way is using ElementName. <Grid DataContext="{Binding ElementName=CustomersList, Path=SelectedItem}"> </Grid>