Changing the User Control of Window dynamically in WPF (MVVM) - wpf

I am working on wpf mvvm pattern.
I have a user control in which i am loading a list of checkboxes in DataGridCheckBoxColumn and binding it to IsSelected property from my viewmodel.
Tha xaml code is like this:
<DataGrid Width="150" Grid.Row="0" Background="LightGray" CanUserAddRows="False" AutoGenerateColumns="False" HorizontalAlignment="Left" Name="dataGridCustomers" ItemsSource="{Binding Path=UsecaseListItems}" CanUserResizeColumns="False" CanUserResizeRows="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding Path=IsSelected,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay,IsAsync=True}" Width="50">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate x:Name="dtAllChkBx">
<CheckBox Name="cbxAll" FontWeight="Bold" Content="All" IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=UserControl },Mode=TwoWay}"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
</DataGridCheckBoxColumn>
<DataGridTextColumn Width="85" Binding="{Binding Path=UsecaseName}" Header="UsecaseName" IsReadOnly="True" >
<DataGridColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Black"></Setter>
</Style>
</DataGridColumn.HeaderStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
My HomeViewModel is like this:
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
private Control _someUserControl;
public Control CCS01
{
get { return _someUserControl; }
set { _someUserControl = value; }
}
private UserControl _content;
internal void SetNewContent(UserControl _content)
{
ContentWindow = _content;
}
public UserControl ContentWindow
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged("ContentWindow");
}
}
For my 1st checkbox (CCS01), I have created a view(CCS01.xaml) which contains a few labels and textboxes in grid format.And its corresponding ViewModel is below:
public class CCS01ViewModel: BaseNotifyPropertyChanged
{
HomeViewModel _homeViewModel;
public ICommand OpenUsersCommand { get; private set; }
public CCS01ViewModel(HomeViewModel mainModel)
{
this._homeViewModel = mainModel;
//this._model = model;
OpenUsersCommand = new RelayCommand(OpenUsers, CanOpenUsers);
}
private void OpenUsers(object _param)
{
//UsersPanelViewModel upmodel = new UsersPanelViewModel(_mainModel, _model);
//UsersPanel up = new UsersPanel();
//up.DataContext = upmodel;
//_mainModel.SetNewContent(up);
}
private bool CanOpenUsers(object _param)
{
return true;
}
}
I want to load the selected checkbox view in the main UserControl(ExecutionDetails.xaml) . Currently, I am loading it with the first Checkbox view in it by default like this:
<StackPanel>
<Grid Name="HostGrid">
<ContentControl Content="{Binding ContentWindow}"/>
</Grid>
</StackPanel>
Please suggest me how to bind the view with respective checkboxes user controls dynamically based on the checkbox selection.
The code behind of this(ExecutionDetails.xaml.cs) is like this:
public partial class ExecutionDetails : UserControl
{
public ExecutionDetails()
{
InitializeComponent();
HomeViewModel viewmodel = new HomeViewModel();
CCS01ViewModel ccViewModel = new CCS01ViewModel(viewmodel);
CCS01 obj = new CCS01();
obj.DataContext = ccViewModel;
viewmodel.ContentWindow = obj;
this.DataContext = viewmodel;
}
}

Related

How to use GridViewComboBoxColumn and allow the user to edit?

I need to present a WPF GridView where one column is a Combobox, The user can select one value from the list or enter a new value so I set the IsComboBoxEditable to true but the problem is that if the user types a value that is not in the ItemsSource the Text is blank when the Combobox looses the focus.
Note : I don't want, when a new value is typed , this value to be
added to the ItemsSource. I only need to save it's string value in row
that bounded to it.
I also need DropDownOpened event, to populate it's ItemsSource.
Here is my code:
<telerik:GridViewDataColumn Header="Description">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<telerik:RadComboBox IsEditable="True" ItemsSource="{Binding Descriptions}" Text="{Binding Description1,Mode=TwoWay}" DropDownOpened="descriptionRadComboBox_DropDownOpened"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
Description1 is string property, and Descriptions is List of string that populate in runtime.(When DropDownOpened Event occurred)
Like you mentioned, your goal is, simply, to "Editable ComboBox".
(And, of course, you don't want to add new Item to ItemsSource)
<telerik:GridViewDataColumn UniqueName="description1" Header="Description">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description1}"></TextBlock>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
<telerik:GridViewDataColumn.CellEditTemplate>
<DataTemplate>
<telerik:RadComboBox Name="SLStandardDescriptionsRadComboBox" IsEditable="True"
ItemsSource="{Binding DataContext.SLStandardDescriptions, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
DisplayMemberPath="SLStandardDescriptionTitle" DropDownOpened="Description_DropDownOpened">
</telerik:RadComboBox>
</DataTemplate>
</telerik:GridViewDataColumn.CellEditTemplate>
</telerik:GridViewDataColumn>
Codebehinde :
private void RadGridView_CellEditEnded(object sender, GridViewCellEditEndedEventArgs e)
{
if (e.Cell.Column.UniqueName == "description1")
{
RadComboBox combo = e.Cell.ChildrenOfType<RadComboBox>().FirstOrDefault();
if (combo != null)
{
List<Description> comboItems = combo.ItemsSource as List<Description>;
string textEntered = e.Cell.ChildrenOfType<RadComboBox>().First().Text;
bool result = comboItems.Contains(comboItems.Where(x => x.DescriptionTitle == textEntered).FirstOrDefault());
if (!result)
{
comboItems.Add(new Description { DescriptionTitle = textEntered });
combo.SelectedItem = new Description { DescriptionTitle = textEntered };
}
if (_viewModel.AccDocumentItem != null)
{
if (e.Cell.Column.UniqueName == "description1")
_viewModel.AccDocumentItem.Description1 = textEntered;
}
}
}
}
Here is the solution for .net DataGrid control:
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Title}" ></DataGridTextColumn>
<DataGridComboBoxColumn SelectedValueBinding="{Binding ComboItem.ID}" DisplayMemberPath="ComboTitle" SelectedValuePath="ID">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.ComboItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.ComboItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<Setter Property="IsEditable" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
Surely you can do this using Telerik DataGrid control as well.
And here is my ViewModel:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
ComboItems = new ObservableCollection<ComboItem>()
{
new ComboItem(){ID=1,ComboTitle="ComboItem1"},
new ComboItem(){ID=2,ComboTitle="ComboItem2"},
new ComboItem(){ID=3,ComboTitle="ComboItem3"}
};
Items = new ObservableCollection<Item>()
{
new Item(){ID=1,Title="Item1",ComboItem=ComboItems[0]},
new Item(){ID=2,Title="Item2",ComboItem=ComboItems[1]},
new Item(){ID=3,Title="Item3",ComboItem=ComboItems[2]}
};
}
public ObservableCollection<Item> Items { get; set; }
public ObservableCollection<ComboItem> ComboItems { get; set; }
}
public class Item
{
public int ID { get; set; }
public string Title { get; set; }
public ComboItem ComboItem { get; set; }
}
public class ComboItem
{
public int ID { get; set; }
public string ComboTitle { get; set; }
}

Enable button when checkbox is checked in WPF datagrid

I have a Datagrid in WPF in which first column has checkbox column and last column has buttons.
Initially, I want to make all the buttons disabled and whenever any checkbox is checked then button of that row should get enabled.
checkbox is unchecked then button should be disabled.
Searched a Lot but could not find anything related to this.
I am not using MVVM.. How to do this on the code behind?
Thanks
This is my Xaml Code and I am simply assigning my itemsource on the code behind
<Grid Grid.Row="2" Grid.ColumnSpan="2" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" >
<Border BorderThickness="0" Margin="10" CornerRadius="15">
<Border.BitmapEffect>
<DropShadowBitmapEffect />
</Border.BitmapEffect>
<Grid>
<Border x:Name="BDRounded" BorderThickness="0" CornerRadius="15" Background="White"/>
<DataGrid HorizontalAlignment="Left" x:Name="dgrdActors" RowHeight="74" AutoGenerateColumns="False" CanUserAddRows="False"
BorderThickness="1,0,0,0" BorderBrush="#FFD1A251" FontSize="28" Foreground="#DCA566" FontFamily="Helvetica Neue"
CanUserResizeRows="False" AlternatingRowBackground="Linen" AlternationCount="2" Background="#DCA566"
RowHeaderWidth="0" CanUserResizeColumns="False" CanUserSortColumns="False" CanUserReorderColumns="False"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible"
HorizontalGridLinesBrush="#FFD1A251" VerticalGridLinesBrush="#FFD1A251" Height="326"
SelectionMode="Extended" SelectionUnit="FullRow" VirtualizingStackPanel.VirtualizationMode="Standard"
Style="{StaticResource DatagridStyle}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="70" CanUserReorder="False" CanUserResize="False" CanUserSort="False" CellStyle="{StaticResource HitVisibilityCellStyle}" HeaderStyle="{StaticResource HeaderStyle}" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Viewbox Margin="-1">
<!--<CheckBox x:Name="chkboxactors" HorizontalAlignment="Center" VerticalAlignment="Center"
IsChecked="{Binding IsActorChecked, UpdateSourceTrigger=PropertyChanged}"></CheckBox>-->
<CheckBox x:Name="chkboxActors" HorizontalAlignment="Center" VerticalAlignment="Center"></CheckBox>
</Viewbox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Actor Name(s)" Width="300" Binding="{Binding ActorName}" CanUserReorder="False" CellStyle="{StaticResource CellStyle}" CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Role(s)" Width="300" Binding="{Binding Role}" CanUserReorder="False" CellStyle="{StaticResource CellStyle}" CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Width="250" CanUserReorder="False" CanUserResize="False" HeaderStyle="{StaticResource HeaderStyle}" CellStyle="{StaticResource HitVisibilityCellStyle}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnSelectRole" Content="Select Role" Style="{StaticResource DatagridButtonStyle}"></Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=BDRounded}"/>
</DataGrid.OpacityMask>
</DataGrid>
</Grid>
</Border>
</Grid>
Here you go!
There's no need to do the enabling/disabling in the ViewModel, as this can all be done in XAML.
XAML:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="100">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="false"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked}" Value="true">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel:
public class ViewModel
{
public List<Data> Items { get; private set; }
public ViewModel()
{
Items = new List<Data>
{
new Data(),
new Data(),
new Data()
};
}
}
public class Data : INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get {return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
Edit:
Since you've requested a code-behind implementation, here you go. This works by traversing the visual tree based on the current row that the checkbox was clicked from.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DataGrid AutoGenerateColumns="False" x:Name="MyDataGrid">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Click="CheckBox_Clicked"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="100" x:Name="Button" IsEnabled="false" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
XAML.CS:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyDataGrid.ItemsSource = new List<string>
{
"test",
"test1",
"test2",
"test3"
};
}
private void CheckBox_Clicked(object sender, RoutedEventArgs e)
{
var checkBox = sender as CheckBox;
if (checkBox != null)
{
var associatedRow = VisualTreeHelper.GetParent(checkBox);
while ((associatedRow != null) && (associatedRow.GetType() != typeof(DataGridRow)))
{
associatedRow = VisualTreeHelper.GetParent(associatedRow);
}
var dataGridRow = associatedRow as DataGridRow;
if (dataGridRow != null)
{
var associatedButton = FindChild(dataGridRow, "Button");
if (associatedButton != null)
{
associatedButton.IsEnabled = checkBox.IsChecked.HasValue ? checkBox.IsChecked.Value : false;
}
}
}
}
public static Button FindChild(DependencyObject parent, string childName)
{
if (parent == null) return null;
Button foundChild = null;
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var childType = child is Button;
if (!childType)
{
foundChild = FindChild(child, childName);
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
if (frameworkElement != null && frameworkElement.Name == childName)
{
foundChild = (Button)child;
break;
}
}
else
{
foundChild = (Button)child;
break;
}
}
return foundChild;
}
}
If the datagrid is bound to a collection of objects you own (ideally in this case a Facade of a model), then add a IsSelected property to the object that makes up the collection. You can databind your checkbox to that property.
To enable/disable the button, have the model/facade in the collection implement ICommand. You can then use the CanExecute method to enable/disable the button based on the value of IsSelected.
public class User : ICommand, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool IsSelected
{
get
{
return this.isSelected;
}
set
{
this.isSelected = value;
CommandManager.InvalidateRequerySuggested();
this.OnPropertyChanged("IsSelected");
}
}
public bool CanExecute(object parameter)
{
return this.IsSelected;
}
public void Execute(object parameter)
{
// ... Do stuff ...
}
private void RaiseCanExecuteChanged()
{
var handler = this.CanExecuteChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(property));
}
private void OnPropertyChanged(string property)
{
var handler = this.PropertyChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(property));
}
}
Now you bind your checkbox to the IsSelected property. Anytime that the checkbox is selected, the CanExecute method will fire on the class.
Ideally you would use a DelegateCommand class from either MVVMLight or Prism, which have a RaiseCanExecuteChanged() method. This lets you avoid using the CommandManager to requery it.
This can be your DataGrid definition:
<DataGrid x:Name="TestDataGrid" ItemsSource="{Binding source}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="TestBox" Content="Test" IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Click" IsEnabled="{Binding IsChecked}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Pretty simple code behind:
public partial class MainWindow : Window
{
public ObservableCollection<Model> source { get; set; }
public MainWindow()
{
InitializeComponent();
source = new ObservableCollection<Model>();
source.Add(new Model());
source.Add(new Model());
this.DataContext = this;
}
}
This could be your model:
public class Model : DependencyObject
{
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(Model), new PropertyMetadata(false));
}
Or implement INPC inteface:
public class Model : INotifyPropertyChanged
{
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; }
set
{
_IsChecked = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
< DataGridTemplateColumn Header="{ Loc CellSettings_Min}" >
<DataGridTemplateColumn.CellTemplate >
< DataTemplate >
< TextBox Width="100" Text="{ Binding Interval }" >
<TextBox.Style>
< Style TargetType = "TextBox" >
< Setter Property="IsEnabled" Value="false" />
< Style.Triggers >
< DataTrigger Binding = "{ Binding AutoScale }" Value= "True" >
< Setter Property = "IsEnabled" Value = "False" />
< /DataTrigger >
< DataTrigger Binding = "{ Binding AutoScale }" Value="False" >
< Setter Property = "IsEnabled" Value = "True" />
</ DataTrigger >
</ Style.Triggers >
</ Style >
</ TextBox.Style >
</ TextBox >
</ DataTemplate >
</ DataGridTemplateColumn.CellTemplate >
</ DataGridTemplateColumn >
You should use MVVM.DataBinding is convenient .
Xaml
<CheckBox Name="checkbox" IsChecked="{Binding Checked, Mode = TwoWay}" />
<Button IsEnabled="{Binding ButtonEnabled , Mode = TwoWay}" />
C#
In ViewModel
public class ViewMode : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _buttonChecked = false;
public bool ButtonChecked
{
get
{
return _buttonChecked;
}
set
{
if(value == true)
{
_buttonChecked = value;
OnPropertyChanged("ButtonChecked");
}
}
}
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
if(value == true)
{
_checked= value;
ButtonChecked = value;
OnPropertyChanged("Checked");
}
}
}
[NotifyPropertyChangedInvocator]
private virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

How to notify viewmodel collection that property on model class has changed

I have a class that has a boolean property called IsChecked.
A collection of this class exist in my viewmodel. I've bound a datagrid in my view to this collection. I need to call a method in my viewmodel when the checkbox in the view gets changed. I've implemented INotifyPropertyChanged on the class and it is firing when I check the box but I don't know how to call the method in my viewmodel.
Here's the class in my model...
public class AccountComponent : INotifyPropertyChanged
{
public string Name { get; set; }
public decimal Amount { get; set; }
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
public bool Enabled { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Here's the collection in my viewmodel...
private ObservableCollection<AccountComponent> _accountComponents;
private string _accountStatus;
public ObservableCollection<AccountComponent> AccountComponents
{
get { return _accountComponents; }
set
{
_accountComponents = value;
NotifyPropertyChanged("AccountComponents");
CalculateComponentTotal();
}
}
Here's my XAML in the view...
<DataGrid ItemsSource="{Binding AccountComponents}" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<CheckBox IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" IsEnabled="{Binding Enabled}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Name}" Header="Component" Width="*" IsReadOnly="True" ElementStyle="{DynamicResource TextBlock-Sketch}"/>
<DataGridTextColumn Binding="{Binding Amount,StringFormat={}{0:C}}" IsReadOnly="True" Header="Charge" ElementStyle="{DynamicResource TextBlock-Sketch}">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Since AccountComponent implements INPC you can observe the IsChecked property in your VM.
say in your VM constructor:
AccountComponents = new ObservableCollection<AccountComponent>();
AccountComponents.CollectionChanged += AccountComponentsOnCollectionChanged;
...
private void AccountComponentsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) {
if (args.NewItems != null && args.NewItems.Count != 0)
foreach (AccountComponent account in args.NewItems)
account.PropertyChanged += AccountOnPropertyChanged;
if (args.OldItems != null && args.OldItems.Count != 0)
foreach (AccountComponent account in args.OldItems)
account.PropertyChanged -= AccountOnPropertyChanged;
}
private void AccountOnPropertyChanged(object sender, PropertyChangedEventArgs args) {
if (args.PropertyName == "IsChecked")
// Invoke Your VM Function Here
}
That should be it.
In Xaml:
add the following namspace..
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Now for you checkbox add the following code:
<CheckBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding CheckedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
In ViewModel:
public ICommand CheckedCommand
{
get
{
return new DelegateCommand(OnChecked);//Delegate command is the Implemntation of Icommand Interface
}
}
public void OnLogin(object param)
{
//code for you checked event
}
Hope this will help you.

WPF: How to use Style.Triggers

I want to implement (file) Explorer like icon display. The items have date and label.
User should be able to edit the label:
Select an item
Click on label
Label's TextBlock is replaced with TextBox for editing
How to end editing (just for info):
Click anywhere outside of the TextBox
Press Enter keyboard key (by implementing ICommand?)
1st I tried to set the Visibility of TextBlock and TextBox in code found out it is not the 'right' way to to do. Maybe it is possible to edit item's Label using (Data)Triggers?
I can track the OnClickLabelBlock and set selectedMedia.IsEditing = true; but it does not fire the trigger.
Any idea why MediaItem.IsEditing property value change is notifying the DataTrigger? Is it something to do with the order of execution or priority mechanism?
I will pick the answer which guides me to the 'best' architecture to solve it.
Thanks.
XAML:
<Window x:Class="WPFComponents.DailyImages"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Model="clr-namespace:WPFComponents.Model"
Title="Media Items" Height="300" Width="300">
<ListView x:Name="_mediaItemList" ItemsSource="{Binding MediaItems}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Multiple">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate DataType="Model:MediaItem">
<Grid Width="80" Margin="4">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Image HorizontalAlignment="Center" Stretch="Uniform" Source="{Binding Path=IconPath}" Width="70" />
<StackPanel Grid.Row="2">
<TextBlock Text="{Binding Path=Date}" TextWrapping="Wrap" />
<TextBlock x:Name="_labelTextBlock" Text="{Binding Path=Label}" TextWrapping="Wrap"
PreviewMouseLeftButtonDown="OnClickLabelBlock">
</TextBlock>
<TextBox x:Name="_labelTextBox" Text="{Binding Path=Label}" Visibility="Collapsed"
TextWrapping="WrapWithOverflow" TextAlignment="Center">
</TextBox>
</StackPanel>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsEditing}" Value="True">
<Setter TargetName="_labelTextBlock" Property="Visibility" Value="Collapsed" />
<Setter TargetName="_labelTextBox" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" VerticalAlignment="Top" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Source:
public partial class DailyImages
{
public DailyImages()
{
InitializeComponent();
ViewModel.DailyImages dailyImages = new ViewModel.DailyImages();
// DailyImages has ObservableCollection<MediaItem> MediaItems property
_mediaItemList.DataContext = dailyImages;
}
private void OnClickLabelBlock(object sender, MouseButtonEventArgs e)
{
TextBlock notes = sender as TextBlock;
if (notes == null)
return;
MediaItem selectedMedia = notes.DataContext as MediaItem;
if (selectedMedia == null)
{
// TODO: Throw exception
return;
}
_mediaItemList.SelectedItems.Clear();
selectedMedia.IsSelected = true;
selectedMedia.IsEditing = true;
}
public class MediaItem
{
public MediaItem()
{
IsEditing = false;
IsSelected = false;
}
public DateTime Date { get; set; }
public string Label { get; set; }
public string IconPath { get; set; }
public bool IsEditing { get; set; }
public bool IsSelected { get; set; }
}
References:
Dependency Property Value Precedence
Part II: ListView & File Explorer Like Behaviour
MediaItem must implement INotifyPropertyChanged and each of its properties that must be bound, must call RaisePropertyChanged in order for the binding to work correctly. In your case, the Binding on IsEditing has no way to know that the value has changed.
To bind your IsEditing property, WPF has to be notified when it is modified.
Then you have to implement INotifyPropertyChanged in MediaItem. (Or add dependency properties)
public class MediaItem : INotifyPropertyChanged
{
public MediaItem()
{
IsEditing = false;
IsSelected = false;
}
// Use the same pattern for Date, Label & IconPath if these value may change after the MediaItem instance has been added to the collection MediaItems.
public DateTime Date { get; set; }
public string Label { get; set; }
public string IconPath { get; set; }
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
private bool isEditing;
public bool IsEditing
{
get { return isEditing; }
set
{
if (isEditing != value)
{
isEditing = value;
OnPropertyChanged("IsEditing");
}
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Otherwise, your code is correct.

styling nth item in listview?

I have an external 'current' property (int) which represents the current index of a collection. I have a listview that displays this collection. I want to be able to style the 'nth' item of the collection depending on the value of 'current', i.e. if current is 3, highlight the 4th item in the collection (index = 3), etc. How can I do this?
An alternative would be to bind when the item text is equal to another external property.
To customize the style of a ListView, you can create a DataTrigger that binds to a property of the View's DataContext to alter the current style. In this example the code alters the Background.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Aqua"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.StyleType, RelativeSource={RelativeSource Self}}" Value="1">
<Setter Property="Background" Value="Chartreuse"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
I've added most of the code here so you can get as complete picture as possible of what is happening, but I skipped the common MVVM base classes.
How it works:
ListView ItemsSource binds to Customers
ListView SelectedItem binds to Customer
ListView.ItemContainerStyle has a DataTrigger that binds to DataContext.StyleType
DataContext is a List of Customer objects with a user defined StyleType property that gets initialized in the code-behind
A Button Command clears the value of StyleType
Clicking a row changes the style of the next row
Customer implements INotifyPropertyChanged, that fires when StyleType changes
DataTrigger alters the Background each ListViewItem
Here is the XAML, look at DataTrigger:
<Window x:Class="ListViewScrollPosition.Views.ScrollBarwindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Style on Model" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView
Grid.Row="0"
SelectedItem="{Binding Customer}"
ItemsSource="{Binding Customers}" x:Name="myListView">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="Aqua"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.StyleType, RelativeSource={RelativeSource Self}}" Value="1">
<Setter Property="Background" Value="Pink"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="First Name"
DisplayMemberBinding="{Binding FirstName}" />
<GridViewColumn Header="Last Name"
DisplayMemberBinding="{Binding LastName}" />
<GridViewColumn Header="Style Type"
DisplayMemberBinding="{Binding StyleType}" />
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="1" Content="Change Style" Command="{Binding Path=AlterStyle}"/>
</Grid>
</Window>
Here is the ViewModel the View uses to get Customers and to alter the value of StyleType:
using System.Collections.Generic;
using System.Windows.Input;
using ListViewScrollPosition.Commands;
using ListViewScrollPosition.Models;
namespace ListViewScrollPosition.ViewModels
{
public class MainViewModel : ViewModelBase
{
private DelegateCommand _alterStyleCommand;
public MainViewModel()
{
}
public ICommand AlterStyle
{
get
{
if (_alterStyleCommand == null)
{
_alterStyleCommand = new DelegateCommand(AlterStyleCommand);
}
return _alterStyleCommand;
}
}
private void AlterStyleCommand()
{
foreach (var customer in Customers)
{
customer.StyleType = 0;
}
}
private void ApplyStyleToNextRow(Customer currentCustomer)
{
bool setNext = false;
foreach (var customer in Customers)
{
if (setNext)
{
customer.StyleType = 1;
setNext = false;
}
else
{
customer.StyleType = 0;
}
if (currentCustomer == customer)
{
setNext = true;
}
}
}
private List<Customer> _customers = Customer.GetSampleCustomerList();
public List<Customer> Customers
{
get
{
return _customers;
}
}
private Customer _customer = null;
public Customer Customer
{
get
{
return _customer;
}
set
{
_customer = value;
ApplyStyleToNextRow(_customer);
OnPropertyChanged("Customer");
}
}
}
}
Here is the Model, ViewModelBase implements INotifyPropertyChanged, StyleType fires OnPropertyChanged when changed which updates each ListViewItem:
using System;
using System.Collections.Generic;
namespace ListViewScrollPosition.Models
{
public class Customer : ViewModels.ViewModelBase
{
public String FirstName { get; set; }
public String LastName { get; set; }
private int _style;
public int StyleType
{
get { return _style;}
set
{
_style = value;
OnPropertyChanged("StyleType");
}
}
public Customer(String firstName, String lastName, int styleType)
{
this.FirstName = firstName;
this.LastName = lastName;
this.StyleType = styleType;
}
public static List<Customer> GetSampleCustomerList()
{
return new List<Customer>(new Customer[4] {
new Customer("A.", "Zero", 0),
new Customer("B.", "One", 1),
new Customer("C.", "Two", 2),
new Customer("D.", "Three", 1)
});
}
}
}
Here is the code-behind where I setup the DataContext:
using System.Windows;
namespace ListViewScrollPosition.Views
{
public partial class ScrollBarwindow : Window
{
public ScrollBarwindow()
{
InitializeComponent();
DataContext = new ViewModels.MainViewModel();
}
}
}

Resources