Multiple Listviews with only one SelectedItem - c#

I have multiple Listviews each binded with their own itemsource. But i only have 1 Selected Item.
So for example i have 5 listboxes (Monday, Tuesday, ...) each of them with their own itemssource (MondayList, TuesdayList, ...). Each of these Listviews SelectedItem property is binded to The Property 'CurrentToDo'.
The problem is that if i select one in the Monday and then select one in Tuesday they are both selected.
So only one item should be able to be selected throughout all the listviews, please help.
<UserControl.Resources>
<Style x:Key="ListViewItemStyleToDo" TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
<GridView x:Key="ViewBase1" x:Shared="False" >
<GridViewColumn Header="" Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Subject" Width="auto" DisplayMemberBinding="{Binding Subject}" />
</GridView>
</UserControl.Resources>
<ListView Grid.Row="1" ItemsSource="{Binding MondayList}" SelectedItem="{Binding CurrentToDo, Mode=TwoWay}" SelectionMode="Single" ItemContainerStyle="{DynamicResource ListViewItemStyleToDo}" View="{DynamicResource ViewBase1}" />
<ListView Grid.Row="3" ItemsSource="{Binding TuesdayList}" SelectedItem="{Binding CurrentToDo,Mode=TwoWay}" SelectionMode="Single" ItemContainerStyle="{DynamicResource ListViewItemStyleToDo}" View="{DynamicResource ViewBase1}" />
Property:
private ToDoMod _currentToDo;
public ToDoMod CurrentToDo
{
get { return _currentToDo; }
set
{
_currentToDo= value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("CurrentToDo"));
}
}

Here is the answer:
Just bind to SelectedValue instead of SelectedItem.

You'd need to add 5 more properties: MondayCurrentToDo, TuesdayCurrentToDo...
Bind each list view's SelectedItem to the respective property.
In setter of each property, update CurrentToDo and remove selection of other lists - so it will look like this:
private ToDoMod _mondayCurrentToDo;
public ToDoMod MondayCurrentToDo
{
get { return _mondayCurrentToDo; }
set
{
_mondayCurrentToDo= value;
CurrentToDo = _mondayCurrentToDo;
_tuesdayCurrentToDo = null;
//TODO: add null setters for all other _<dayOfWeek>CurrentToDo members
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MondayCurrentToDo"));
PropertyChanged(this, new PropertyChangedEventArgs("TuesdayCurrentToDo"));
//TODO: add notificaitons for all other <DayOfWeek>CurrentToDo properties
}
}
}
Of course, refactor this into methods, etc...

Related

Wpf multiple combobox binding to one property

I am having a data grid, one of its cells is a combo box like:
<DataGrid x:Name="Applications" RowStyle="{StaticResource CollapsedRow}" AutoGenerateColumns="false" CanUserAddRows="false" ItemsSource="{Binding Applications}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content='˅' FontSize="9" Name="ExpanderButton" Click="OnGroupChange" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="181" Header="Name" Binding="{Binding Name, Mode=OneWay}" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Cabins,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
SelectedValuePath="Id" IsSynchronizedWithCurrentItem="True"
SelectedValue="{Binding Path=DataContext.SelectedCabin,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
mah:TextBoxHelper.Watermark="{Binding Path=DataContext.CabinsWatermark, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
Height="2" Width="300" Margin="10 5 10 10" HorizontalAlignment="Left">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource GuidConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
And as you see in each row there ia a combo box in detail row (expanded row using button), each combo box is binded to one property:
private Guid? selectedCabin;
public override Guid? SelectedCabin
{
get => selectedCabin;
set
{
selectedCabin = value;
if (value.HasValue)
{
Console.WriteLine(value);
}
OnPropertyChanged();
}
Now problem is when i select item in combo box i am getting not single value but couple of them (I suppose there are all values from one combo box I made a selection on), to make sure i double checked with test code behind:
private void ComboBox_OnSelectCabinChanged(object sender, RoutedEventArgs e)
{
var combo = (ComboBox)sender;
if (combo != null && combo.IsDropDownOpen)
{
((ApplicationsViewModel)DataContext).SelectedCabin = (Guid?)sender;
combo.IsDropDownOpen = false;
}
}
And I am getting here and combo box item list and casting exception. What could be the root cause of this and is there a way to bind multiple combo box values to one property, so is i select one it will override another.
It seems like you are binding the SelectedValue of all row details ComboBoxes to the same source property. And you can't cast the sender argument to a Guid?. Try to cast the SelecteedValue property of the ComboBox:
SelectedCabin = (Guid?)combo.SelectedValue;
If you don't want to handle the SelectionChanged event in the view, you could use an interaction trigger that executes a command that sets the source property. Please refer to this blog post for more information about this.

Setting the needed index of a Combobox after a method call C# WPF

I'm developing an MVVM application and I have some logic regarding adding the items to the Combobox placed in DataGridTemplateColumn. Each generated Combobox is binded to a Observablecollection which is refreshed whenever the Datagridrow changes. There are only two items in the Combobox DropDownList. Since the collection is a property of the class needed for each row the ComboBox binding and DropDownList items show up as intened. The one with index [0] is a default value,editable. In index [1] I have a SumProduct of some properties of the mentioned class - which works and shows in the DropDownList of the specific ComboBox.
The thing I want to achive is to have this [1] item come up on the ComboBox when one of the method to count this SumProducts is called.
XAML:
<DataGridTemplateColumn Header="PRĄD POJEMNOŚCIOWY [A]" HeaderStyle="{StaticResource PRAD_POJEMNOSCIOWY}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<ComboBox Name="PradPojemnosciowyComboBox"
SelectedValue="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=LiniaWyComboBox, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEditable="True"
IsReadOnly="False"
Text="{Binding Prad_pojemnosciowy, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsTextSearchEnabled="False"
IsSynchronizedWithCurrentItem="False"
PreviewKeyDown="PradPojemnosciowyComboBox_OnPreviewKeyDown">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="SelectedValue" Value="{x:Null}">
<Setter Property="SelectedIndex" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
....
/// one of the properties that triggers the method in MainWindow.xaml.cs
<DataGridTemplateColumn Header="DŁUGOŚĆ [km]" >
<DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<TextBox Name="dlugoscTextBox"
Background="{StaticResource navyColor}" Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textBoxInError}"
TextChanged="TextBox_OnTextChanged">
<TextBox.Text>
<Binding Path="długość"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validator:DoubleValidator Min="0" Max="1000"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
PradPojemnosciowyComboBox_OnPreviewKeyDown:
private void PradPojemnosciowyComboBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
ComboBox tb = (ComboBox)sender;
if (tb.Text != null)
{
var value = 0.0;
bool isTry = Double.TryParse(tb.Text, out value);
if (isTry)
{
SelectedItem = value;
var lineSelected = (modelGPZ.GetLineWyList().FirstOrDefault(x => x.isSelected == true));
if (lineSelected != null)
{
var u = e.OriginalSource as UIElement;
if (e.Key == Key.Enter && u != null)
{
lineSelected.Prad_pojemnosciowy = value;
e.Handled = true;
u.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
}
}
refreshLineWy();
if (tb.Text != lineSelected.LiniaWyComboBox[0].ToString())
{
tb.Text = lineSelected.LiniaWyComboBox[0].ToString();
OnPropertyChanged("SelectedItem");
OnPropertyChanged("LiniaWyComboBox");
}
}
}
}
Method that is called counts all the neccessary values and shows them in the specific ComboBox - item [1] from DropDownList. Again - what I need is that I want to show the [1] item from the ComboBox after updating or from what I think - calling the method that updates the values.

How to set "SELECT" option as first option in wpf comboBox

I am using Enum Logic to populate the comboBox. The comboBox is populated correctly. But I want to set "--SELECT--" as the first option.
How can It be done??
put this in your xaml combobox
SelectedIndex="0"
Just modify your list before sending it to the view:
public List<string> YourList
{
get
{
if (_list[0] != "--Select--")
_list = _list.Insert(0, "--Select--");
return _list;
}
}
Try setting null value
<ComboBox SelectedItem="{Binding Item}" ItemsSource="{Binding Items}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="displayText" Text="{Binding}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter TargetName="displayText" Property="Text" Value="--Select--" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
And VM:
public ObservableCollection<string> Items
{
get
{
return new ObservableCollection<string>
{
null,"1","2","3"
};
}
}
public string Item { get; set; }
Probably the simplest way would be to make ComboBox editable so that you can set custom text, and readonly so that items can only be selected:
<ComboBox IsEditable="True" IsReadOnly="True" Text="--SELECT--" />
Note that this will change the look of a control, so if that is a problem for you, you will have to use more complicated solution.
If your Class has a Property you want to display in the ComboBox take a look at following approach:
Set a namespace to your class:
xmlns:models="clr-namespace:Company.ProjectName.Models;assembly=Company.ProjectName.Models"
Then in your ComboBox add a new Item of your class like this:
<ComboBox DisplayMemberPath="Name" SelectedItem="{Binding BindingProperty, Mode=TwoWay}" >
<ComboBox.ItemsSource>
<CompositeCollection>
<models:CustomClass Name="--SELECT--" ID="0"/>
<!--<ComboBoxItem Background="LightGray" IsHitTestVisible="False" Height="1" Margin="7,0,7,0" IsEnabled="False" Focusable="False"/> !-->
<CollectionContainer Collection="{Binding Source={StaticResource ObservableCollection}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
you could add the value(--SELECT--)to the data-table after retrieving data from database and set the selected index to 0.
DataRow row = dt.NewRow();
row["DATA"] = "--SELECT--";
dt.Rows.InsertAt(row, 0);
after bindingcombobox1.SelectedIndex = 0;

DataGridRow.IsSelected is set and I dont know why

Hello this is my problem:
I got two styles defined in the resources for DataGridRow and DataGridCell:
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="DataGridRow.IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected" Value="True">
<Setter Property="Background" Value="LightSteelBlue" />
</Trigger>
</Style.Triggers>
</Style>
I basically want a row to be highlighted blue when user selects it.
I got a MessageWrapper and a SignalWrapper class. Each MessageWrapper has a list of SignalWrappers (which can be empty sometimes). So the first DataGrid is to display all MessageWrappers:
<DataGrid x:Name="messageGrid"
Grid.Column="0"
Margin="3"
AutoGenerateColumns="False"
CanUserAddRows="False"
HorizontalScrollBarVisibility="Hidden"
IsReadOnly="True"
ItemsSource="{Binding SelectedBusWrapper.MessageWrappers}"
SelectedItem="{Binding SelectedBusWrapper.SelectedMessageWrapper}"
attachedProperties:SelectingItemAttachedProperty.SelectingItem="{Binding SelectedBusWrapper.MessageWrapperToScrollIntoView,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Filter">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox MaxWidth="15"
MaxHeight="15"
CommandParameter="{Binding}"
IsChecked="{Binding Filter}"
Style="{StaticResource CheckBoxStyle}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="9*"
Binding="{Binding Message.Name}"
Header="Name" />
<DataGridTextColumn Width="9*"
Binding="{Binding HexId}"
Header="Id" />
<DataGridTextColumn Width="11*"
Binding="{Binding Message.Sender}"
Header="Sender" />
</DataGrid.Columns>
</DataGrid>
Each Row of this DataGrid has a CheckBox column. If no CheckBox at all is checked then all SignalWrappers are displayed in second DataGrid. If one or more are checked, then only the SignalWrappers of the checkec MessageWrappers should be displayed. Here the DataGrid:
<DataGrid Grid.Column="1"
Margin="3"
AutoGenerateColumns="False"
CanUserAddRows="False"
HorizontalScrollBarVisibility="Hidden"
IsReadOnly="True"
ItemsSource="{Binding SelectedBusWrapper.SelectedSignalWrappers}"
SelectedItem="{Binding SelectedBusWrapper.SelectedSignalWrapper}"
SelectionMode="Extended">
<DataGrid.Resources>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</DataGrid.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding MouseDoubleClickCommand}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Filter">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox MaxWidth="15"
MaxHeight="15"
CommandParameter="{Binding}"
IsChecked="{Binding Filter}"
Style="{StaticResource CheckBoxStyle}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="6*"
Binding="{Binding Signal.Name}"
Header="Name" />
<DataGridTemplateColumn Width="Auto" Header="Value">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox MinWidth="75"
IsEditable="True"
ItemsSource="{Binding Signal.Values.Values}"
SelectedItem="{Binding SelectedValue,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Now if the user selects one or more message(s) in the first DataGrid the signals of those messages should be highlighted in the second. If the user selects a signal in the second, the parent message should be highlighted in the first DataGrid.
Bound Property of MessageWrapper class:
public bool IsSelected
{
get
{
return isSelected;
}
set
{
if (isSelected == value) {
return;
}
isSelected = value;
foreach (var item in SignalWrappers) {
item.IsSelected = value;
}
RaisePropertyChanged("IsSelected");
}
}
Bound Property of SignalWrapper class:
public bool IsSelected
{
get
{
return isSelected;
}
set
{
if (isSelected == value) {
return;
}
isSelected = value;
ParentMessageWrapper.IsSelected = value;
RaisePropertyChanged("IsSelected");
}
}
Now I have an odd behavoir I cannot understand:
When I check the first CheckBox in the first DataGrid:
The message gets highlighted, the second DataGrid shows only the signals of that message and highlights them. This is correct so far.
If I now check a second message it highlights both rows and all signals of those rows. While debugging i noticed what happens is that the first MessageWrappers IsSelectedflag gets set to false, and all its signals flag gets set to false, which is correct. Afterwards the second MessageWrappers flag gets set to true and all its signals flag gets set to true, which is correct too.
And afterwards for a reason I cannot understand a signal of the first MessageWrapper IsSelected flag is set to true, this will set the MessageWrappers flag to true and this will set the other signals flag to true.
Note: If I check the third CheckBox everything is working as expected again.
Can anybody understand why this is? Thanks for your help in advance!
you have many loops when setting IsSelected in SingleWrapper because it set ParentMessageWrapper.IsSelected = value; which will return to set IsSelected in SingleWrapper and so on.
i suppose to make Method in SingleWrapper Like this:
public SetIsSelected(bool value)
{
if (isSelected == value) {
return;
}
isSelected = value;
RaisePropertyChanged("IsSelected");
}
and change item.IsSelected = value; in MessageWrapper to item.SetIsSelected(value)

How to get Listview ItemClick Vallue in Relay Command

Hi i'm working in windows store app with MVVM pattern and i have some problem in catch the listview itemclick value in relay command. Now i got the selected item value.But don't know how to get itemclickValue. Here i have attach my code.
XAML
<ListView x:Name="lstItem" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding ItemList}" Padding="130,0,0,0" SelectedItem="{Binding SelectedItem,Mode=TwoWay}">
<Triggers:Interactions.Triggers>
<Triggers:EventTrigger EventName="SelectionChanged">
<Triggers:InvokeCommandAction Command="{Binding SelectedItemCommand}" CommandParameter="{Binding SelectedItem,Mode=TwoWay}"/>
</Triggers:EventTrigger>
</Triggers:Interactions.Triggers>
</ListView>
ViewModel Code
private Item _selectedItem;
public Item SelectedItem { get { return _selectedItem; } set { _selectedItem = value; NotifyPropertyChanged("SelectedTrends"); } }
private RelayCommand<Item> _selectedItemCommand;
public RelayCommand<Item> SelectedItemCommand
{
get
{
return this._selectedItemCommand
?? (this._selectedItemCommand= new RelayCommand<Item>(item=>
{
MessageDialog messagedialog = new MessageDialog(item.Name,"Test");
messagedialog.ShowAsync();
}));
}
}
There's a bit of redundancy here:
Option 1: Spare the CommandParameter:
private Item _selectedItem;
public Item SelectedItem
{
get { return _selectedItem; }
set { _selectedItem = value; NotifyPropertyChanged("SelectedTrends"); }
}
private RelayCommand _selectedItemCommand;
public RelayCommand SelectedItemCommand
{
get
{
return this._selectedItemCommand
?? (this._selectedItemCommand= new RelayCommand(() =>
{
MessageDialog messagedialog = new MessageDialog(selectedItem.Name,"Test");
messagedialog.ShowAsync();
}));
}
}
and the XAML:
<ListView x:Name="lstItem" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding ItemList}" SelectedItem="{Binding SelectedItem,Mode=TwoWay}" Padding="130,0,0,0">
<Triggers:Interactions.Triggers>
<Triggers:EventTrigger EventName="SelectionChanged">
<Triggers:InvokeCommandAction Command="{Binding SelectedItemCommand}" />
</Triggers:EventTrigger>
</Triggers:Interactions.Triggers>
</ListView>
Option 2: Spare the SelectedItem binding:
<ListView x:Name="lstItem" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding ItemList}" Padding="130,0,0,0">
<Triggers:Interactions.Triggers>
<Triggers:EventTrigger EventName="SelectionChanged">
<Triggers:InvokeCommandAction Command="{Binding SelectedItemCommand}" CommandParameter="{Binding SelectedItem, ElementName=lstItem}"/>
</Triggers:EventTrigger>
</Triggers:Interactions.Triggers>
</ListView>
and remove the SelectedItem property from the ViewModel, unless you need it for something else.
EDIT
If you want to handle the click event on an item, you need to move the behavior to the ItemTemplate DataTemplate parent control, for example the grid in which the controls are placed. This lets you handle the click event on the item.
To resolve the problem I evaluated the setter attributed if there is Null reference. Then it worked fine and the event wasn't thrown anymore choose other elements.
<ListView Name="lstView" ItemsSource="{Binding Path=SimResults}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemCommand}" CommandParameter="{Binding SelectedItem, ElementName=lstView}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="FileUniqueID" Width="Auto" DisplayMemberBinding="{Binding Path=FileUniqueID}" />
<GridViewColumn Header="XML" Width="Auto" DisplayMemberBinding="{Binding Path=XML}" />
<GridViewColumn Header="Request" Width="Auto" HeaderStringFormat="" DisplayMemberBinding="{Binding Path=RequestShort}" />
<GridViewColumn Header="RequestDate" Width="Auto" DisplayMemberBinding="{Binding Path=RequestDate}" />
<GridViewColumn Header="Response" Width="Auto" DisplayMemberBinding="{Binding Path=ResponseShort}" />
<GridViewColumn Header="ResponseDate" Width="Auto" DisplayMemberBinding="{Binding Path=ResponseDate}" />
<GridViewColumn Header="ResendCounter" Width="Auto" DisplayMemberBinding="{Binding Path=ResendCounter}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>

Categories