Check The First Generated RadioButton in An ItemsControl Group - wpf

I want to check the first radiobutton of my list after the loading of my view.
Code
<ItemsControl HorizontalAlignment="Stretch"
ItemsSource="{Binding ListEquipement}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Checked="RadioButton_Checked"
GroupName="Equipement"
Background="{Binding Background}"
Style="{StaticResource selectedButton}"
Command="{Binding DataContext.SelectEquipementCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"
CommandParameter="{Binding}">
<Binding Path="Text" />
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Does the data have a Boolean property which can be used to initiate the IsChecked status? If so I would bind IsChecked to that property.
Then in the ViewModel, set the first one to be true such as
ListEquipment[0].MyIsCheckedProperty = true;

On the RadioButton add a handler to the Loaded event:
<DataTemplate>
<RadioButton Content="{Binding OpName}"
GroupName="Selectables"
Loaded="RBLoaded"/>
</DataTemplate>
Then in the code behind, create a property which is a boolean status which when the status suggests this is the first time, check the radiobox:
public bool HasBeenChecked { get; set; }
private void RBLoaded(object sender, RoutedEventArgs e)
{
if (HasBeenChecked == false)
{
(sender as RadioButton).IsChecked = true;
HasBeenChecked = true;
}
}

Related

CurrentViewModel Property Change Not Reflected in ContentControl

Hello I have looked everywhere and I can't understand what I am doing wrong.
My command is called and the onpropertychanged event is called but the ContentControl in my MainWindow will not show the next view.
I am new to WPF and Xaml and really trying to figure this out.
I have attached a link to the sample application on my Github.
The End game here is I want the Radio button to be checked when the view is active. I also want to be able to change from the home view to other views via a button on that view once I am past this first hurtle.
Github repository
public class ApplicationViewModel : ObservableObject
{
private ICommand _changeViewCommand;
private IViewModel _CurrentViewModel;
private List<IViewModel> _viewModels;
private bool _isActive;
public ApplicationViewModel()
{
ViewModels.Add(new HomeViewModel());
ViewModels.Add(new AccountViewModel());
CurrentViewModel = ViewModels.Find(r => r.Name == "Home");
CurrentViewModel.IsActive = true;
}
public List<IViewModel> ViewModels
{
get
{
if (_viewModels == null)
_viewModels = new List<IViewModel>();
return _viewModels;
}
}
public IViewModel CurrentViewModel
{
get
{
return _CurrentViewModel;
}
set
{
if (_CurrentViewModel != value)
{
_CurrentViewModel = value;
OnPropertyChanged();
}
}
}
public ICommand ChangePageCommand
{
get
{
if (_changeViewCommand == null)
{
_changeViewCommand = new RelayCommand(
p => ChangeViewModel((IViewModel)p),
p => p is IViewModel);
}
return _changeViewCommand;
}
}
private void ChangeViewModel(IViewModel viewModel)
{
CurrentViewModel = ViewModels.Find(r => r.Name == viewModel.Name);
}
public bool IsActive
{
get
{
return _isActive;
}
set
{
_isActive = value;
OnPropertyChanged("IsActive");
}
}
}
<UserControl.DataContext>
<viewModels:ApplicationViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewModels:ApplicationViewModel}"/>
<DataTemplate DataType="{x:Type viewModels:HomeViewModel}">
<views:HomeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:AccountViewModel}">
<views:AccountView/>
</DataTemplate>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<RadioButton Content="Home" IsChecked="{Binding IsActive, Mode=TwoWay}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding }">
<RadioButton.DataContext>
<viewModels:HomeViewModel/>
</RadioButton.DataContext>
</RadioButton>
<RadioButton Content="Account" IsChecked="{Binding IsActive, Mode=TwoWay}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding }">
<RadioButton.DataContext>
<viewModels:AccountViewModel/>
</RadioButton.DataContext>
</RadioButton>
</StackPanel>
you should connect RadioButtons to the ViewModels which you have in the list. don't create new view models in xaml.
and CurrentViewModel can be displayed via ContentControl with one of templates decalred in Resources
<UserControl.DataContext>
<viewModels:ApplicationViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewModels:HomeViewModel}">
<views:HomeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:AccountViewModel}">
<views:AccountView/>
</DataTemplate>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding ViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Name}"
IsChecked="{Binding IsActive}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding }">
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ContentControl Content="{Binding CurrentViewModel}"/>
</StackPanel>
after that you should probably change StackPanel to Grid to allow ContentControl to take all available screen space
Looks like if I assign the AccountViewModel to the button DataContext and set the RelativeSource AncestorType to x:Type Window the button on the HomeView will switch the view to the AccountView.
<Button Height="40" Content="Account" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding }">
<Button.DataContext>
<viewModels:AccountViewModel/>
</Button.DataContext>
</Button>

How to disable deselection of items in ListView?

I've got
<ListView SelectionMode="Single" SelectedIndex="0" ItemsSource="{Binding AccountViewModels}" SelectedItem="{Binding SelectedItem}" Style="{StaticResource AccountsList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<LocalViews:AccountView Margin="{StaticResource ControlMargin}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
Is there a way to disable deselection of an item from the ListView (i.e. ctrl+click)? In other words, I don't want the user to be able to de-select an item, but of course it's OK to select another item.
Since this functionality is purely view/control related it should not be implemented in the view model but you could handle the PreviewMouseLeftButtonDown event of the ListBoxItem container like this:
<ListView SelectionMode="Single" SelectedIndex="0" ItemsSource="{Binding AccountViewModels}" SelectedItem="{Binding SelectedItem}" Style="{StaticResource AccountsList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ItemPreviewMouseLeftButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<LocalViews:AccountView Margin="{StaticResource ControlMargin}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
private void ItemPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ListBoxItem lbi = sender as ListBoxItem;
e.Handled = lbi.IsSelected;
}
One way to handle this would be to use binding and add logic to disallow de-selection in the view model.
From this answer, change the IsSelected as follows:
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
if (value && !isSelected)
{
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}

Binding Comboboxes inside an Itemscontrol

I am having trouble with a combination of ComboBox'es inside an ItemsControl.
What I am trying to do is create a List of ComboBox'es. At the start there is only one default ComboBox with a default value. If you select any other type than the default type, a new ComboBox is added with the default type in it.
Now, each Item in the List actually contains of 2 Combobox'es. The second box displays the number, this type is present.
Before:
After:
Now I want to update the numbers in the second box if any other number in the ItemsControl changes. How do I do that?
Here is the relevant code:
<ItemsControl Grid.Column="0" Grid.Row="2" ItemsSource="{Binding ChosenAppartmentTypeList}" Margin="10">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Margin="10" SelectedIndex="0" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GeneralProjectDataView}}, Path=DataContext.AppartmentTypeList}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GeneralProjectDataView}}, Path=DataContext.ComboBoxSelectedItemChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBox}, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GeneralProjectDataView}}, Path=DataContext.NumberList}" SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}, Path=SelectedItem.Count}" SelectedIndex="0">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Those are the lists:
public ObservableCollection<AppartmentType> AppartmentTypeList { get; set; }
public ObservableCollection<AppartmentType> ChosenAppartmentTypeList { get; set; }
And those are the properties:
public class AppartmentType : ValidatableBindableBase
{
private string name;
private int count;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
public int Count
{
get { return count; }
set { SetProperty(ref count, value); }
}
}
First, you can observe your property ChosenAppartmentTypeList via PropertyChanged event and update the ApartmentType items.
Reference
Secondly, your SelectedItem on second combobox must be bound two-way.

Silverlight: binding a button command within a DataGrid header within an ItemsControl

I have a bit of a problem binding a button command to a property of an "outer" data context.
The image below shows the layout I have. I'm trying to bind the CommandParameter of the "Clear" button (outlined in red) to the LocationId. The ItemsControl repeats an ObservableCollection of Locations (see below for Location definition).
The Clear button is basically trying to REMOVE the Addresses attached to the Location, to clear down the DataGrid. To do that I need to pass the LocationId to the ViewModel.
The actual command is triggered perfectly but the binding on the CommandParameter isn't quite right.
Here are the underlying classes of Location & Address:
class Location
{
int Id;
ObservableCollection<Address> Addresses;
}
class Address
{
string AddressText;
}
And here is the XAML commented with three alternative attempts & error messages:
<ItemsControl ItemsSource="{Binding Locations, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Id}"/>
<sdk:DataGrid x:Name="ResponseDataGrid" ItemsSource="{Binding Addresses}">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Response" Width="*" Binding="{Binding AddressText}"/>
<sdk:DataGridTemplateColumn Width="100">
<sdk:DataGridTemplateColumn.HeaderStyle>
<Style TargetType="sdk:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Content="Clear"
Command="{Binding DataContext.ClearLocationCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
<!--System.Windows.Data Error: BindingExpression path error: 'Id' property not found on 'System.Windows.Controls.ItemsControl' -->
<!--CommandParameter="{Binding ItemsSource.Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>-->
<!--System.Windows.Data Error: BindingExpression path error: 'Id' property not found on 'System.Collections.ObjectModel.ObservableCollection`1[UI.Location]' -->
<!--This gives me the ID but uses a specific index so only works for the first repeated Location-->
<!--CommandParameter="{Binding ItemsSource[0].Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>-->
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</sdk:DataGridTemplateColumn.HeaderStyle>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Accept">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction
Command="{Binding DataContext.SelectedAddressCommand , RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=sdk:DataGrid}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
From the errors, it seems that the button can't quite see the repeated Location object, it can either see the collection of Locations or a specific indexed Location but not the repeated, generated Location that I NEED!
Thanks!
So you have a ViewModel with an ObservableCollection<Location> And clicking on Clear clears the ObservableCollection<Address> of the specified Location, right?
Why don't you just place the Clear command into the Location class? I dont know about the logic triggered by this command, but doing so you will be able to acces to the correct location Id property.
Edit: here it is my example working:
Classes
public class Location
{
public Location(int id, IEnumerable<Address> addresses)
{
this.Id = id;
this.Addresses =new ObservableCollection<Address>(addresses);
this.ClearLocationCommand = new RelayCommand<int>(e => MessageBox.Show(string.Format("Clear command called on Location {0}", this.Id)));
}
public int Id { get; set; }
public ObservableCollection<Address> Addresses { get; set; }
public ICommand ClearLocationCommand { get; set; }
}
public class Address
{
public Address(string text)
{
this.AddressText = text;
}
public string AddressText { get; set; }
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainWindowViewModel()
{
Locations = new ObservableCollection<Location>(new []
{
new Location(1, new [] { new Address("A1") }),
new Location(2, new [] { new Address("A2"), new Address("A3"), }),
});
}
public ObservableCollection<Location> Locations { get; set; }
private void OnPropertyChanged(string porpertyName)
{
var e = this.PropertyChanged;
if (e != null)
{
e(this, new PropertyChangedEventArgs(porpertyName));
}
}
}
XAML
<Window x:Class="TestBindingButtons.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:TestBindingButtons="clr-namespace:TestBindingButtons" Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<TestBindingButtons:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding Locations, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Id}"/>
<DataGrid x:Name="ResponseDataGrid" ItemsSource="{Binding Addresses}">
<DataGrid.Columns>
<DataGridTextColumn Header="Response" Width="*" Binding="{Binding AddressText}"/>
<DataGridTemplateColumn Width="100">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Content="Clear"
Command="{Binding DataContext.ClearLocationCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Accept">
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
PD:
I didn't posted the implementation of RelayCommand, use your own.
I didn't used your "sdk" framework, just plain microsoft stuff.
If you move the ClearLocationCommand property to the Location class, remember to change the following bindings:
<Button Content="Clear"
Command="{Binding DataContext.ClearLocationCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
/>
to:
<Button Content="Clear"
Command="{Binding ClearLocationCommand}"
CommandParameter="{Binding Id}"
/>
Because each outer Location object now has its own clear command, and you are already on the correct DataContext in that item row.

wpf listbox checkbox combination, really stuck

I am trying to link the checkbox and listview together, and then use the binding method to set the path to the object in order to set the check-box IsChecked status in the listbox.
List<Test1> datas = new List<Test1>();
var data = new Test1 {Key = 1, Value = "Hello", IsSelected= true};
datas.Add(data);
data = new Test1 {Key = 2, Value = "Hello2", IsSelected= false};
datas.Add(data);
What I need to happen is that if the checkbox is checked ( IsSelected is true ) then I need to populate with those values and then when I click and unclick the checkbox in the GUI I need it to also select the proper listeview item so I can get to the tag property.
This code below does not set the checkbox IsChecked.
<ListBox ItemsSource="{Binding}" Name="lstSwimLane" Width="225" Height="125" SelectionChanged="lstSwimLane_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected, Mode=TwoWay}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked" />
<TextBlock Text="{Binding Path=Value}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What do I need to change in order to set the check box to the value in the object? I even tried INotifyChange etc...
Here is the object as well.
public class Test1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isChecked;
public int Key { get; set; }
public string Value { get; set; }
public bool IsSelected
{
get { return _isChecked; }
set
{
if(_isChecked != value)
{
_isChecked = value;
OnPropertyChanged("IsSelected");
}
}
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
I am new to all this wpf, and I am stumped.
Thanks.
Do you need this to work "three-way"? So setting any of the three properties
ListBoxItem.IsSelected
CheckBox.IsChecked
Item1.IsSelected
will affect both of the other properties? Unfortunately, there is no such Binding in WPF so you're gonna have to do a little extra work.
Update
As pointed out by Robert Rossney, a much better solution for this is to bind
ListBoxItem.IsSelected to Item1.IsSelected
CheckBox.IsChecked to ListBoxItem.IsSelected
Updated Xaml
<ListBox ItemsSource="{Binding}"
Name="lstSwimLane"
SelectionMode="Multiple"
Width="225" Height="125" SelectionChanged="lstSwimLane_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected,
Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}},
Path=IsSelected}">
</CheckBox>
<TextBlock Text="{Binding Path=Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In case anyone is interested, here is the markup for the listbox / combox. It will display horizontal.
Thank you all again for all you help as I greatly appreciated it.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding}"
Name="lstSwimLane"
SelectionMode="Multiple"
Width="auto"
Height="auto"
Background="Transparent"
BorderThickness="0"
SelectionChanged="lstSwimLane_SelectionChanged" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsChecked, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="3,3,3,3">
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"
VerticalAlignment="Center"
Margin="0,0,4,0" />
<TextBlock Text="{Binding Value}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Resources