I have 3 columns . One with check box , One with text column and one column with drop down.
I am binding the entire table itemsource to StepTaskViewInfo.CurrentStep.ProjectTasks.Items . StepTaskViewInfo is a variable in my VM and others are nested in it. This works fine .
Only thing that doesnt work is the IsReadOnly property of the FIRST Columns. Am assuming this is some issue because my items source is different and the read only property is different in terms of level of nesting from view model.
For grid :
Items Source = StepTaskViewInfo -> CurrentStep -> ProjectTasks- >Items
For read only propety of each column(which doesnt work) :
IsReadOnly="{Binding StepTaskViewInfo.AreStepsTasksReadonly
StepTaskViewInfo => AreStepsTasksReadonly
<DataGrid RowHeaderWidth="0" x:Name ="TaskDataGrid" Margin="20,0,0,0" ItemsSource="{Binding StepTaskViewInfo.CurrentStep.ProjectTasks.Items}" AutoGenerateColumns="False"
CanUserSortColumns="False" HorizontalAlignment="Left" CanUserAddRows="False" SelectionChanged="TaskRowSelectionChanged"
ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
Background="White" BorderThickness ="0"
ScrollViewer.CanContentScroll="True" Height="240">
<DataGrid.Columns>
<DataGridTemplateColumn Width ="60" HeaderStyle="{StaticResource HeaderStyle}" Header="Selected" IsReadOnly="{Binding StepTaskViewInfo.AreStepsTasksReadonly,UpdateSourceTrigger=PropertyChanged }">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding IsSelected,UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsTaskEnabled,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
//Column 2
<DataGridTextColumn HeaderStyle="{StaticResource HeaderStyle}" Header="Tasks" Width ="*" Binding="{Binding Name}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="AcceptsReturn" Value="true" />
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
//Column 3
<DataGridTemplateColumn HeaderStyle="{StaticResource HeaderStyle}" Header="Status" Width ="130">
<DataGridTemplateColumn.HeaderTemplate >
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="130">
<Label Content ="Status" HorizontalAlignment="Left" Margin ="0,0,0,0"/>
<ComboBox Name ="DefaultStatusComboBox" ItemsSource="{StaticResource Status}" Width="86" DropDownClosed="DefaultStatusComboBox_DropDownClosed" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Height ="26" VerticalAlignment="Top" IsReadOnly ="{Binding StatusIsReadOnly}"
IsEnabled ="{Binding IsSelected}" ItemsSource="{StaticResource Status}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
view model:
public class StepTaskViewModel : INavigationAware, INotifyPropertyChanged
{
private readonly IProjectWorkflowService projectWorkflowService;
private bool isVisible = true;
private readonly IUserService userService;
private string stageId;
private StepTaskViewInfo stepTaskViewInfo;
public StepTaskViewModel(IProjectWorkflowService projectWorkflowService, IUserService userService)
{
this.projectWorkflowService = projectWorkflowService;
this.userService = userService;
StepTaskViewInfo=new StepTaskViewInfo();
}
public StepTaskViewInfo StepTaskViewInfo
{
get { return stepTaskViewInfo; }
set
{
stepTaskViewInfo = value;
OnPropertyChanged();
}
}
// set current step - >load tasks - > set display names for each task --> set drop down source for current step
public string StageId
{
get { return stageId; }
set
{
stageId = value;
StepTaskViewInfo.PeerReview.StageId = stageId;
LoadData();
}
}
#region navigation
public void OnNavigatedTo(NavigationContext navigationContext)
{
StageId =(string) navigationContext.Parameters["StageId"] ;
IsVisible = true;
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
if (!IsVisible)
return;
IsVisible = false;
}
public bool IsVisible
{
get { return isVisible; }
set
{
isVisible = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "" )
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
// called when stage id changes
public void LoadData()
{
var stepTaskViewInfo = projectWorkflowService.LoadProjectStepTaskInfo(StageId);
if (StepTaskViewInfo.CurrentStep != null)
{
StepTaskViewInfo.CurrentStep.ProjectTasks.Items.Clear();
}
StepTaskViewInfo.AllTeamMembers = stepTaskViewInfo.AllTeamMembers;
StepTaskViewInfo.ProjectSteps = stepTaskViewInfo.ProjectSteps;
StepTaskViewInfo.PeerReview = stepTaskViewInfo.PeerReview;
StepTaskViewInfo.AreStepsTasksReadonly = stepTaskViewInfo.AreStepsTasksReadonly;
StepTaskViewInfo.PeerReview.Documents.Items.Add(new ActivityArtifact { FileName = string.Empty });
}
private string GetAliases(ObservableCollection<SelectableTeamMember> selectedStepMembers)
{
string aliases= selectedStepMembers.Aggregate("", (current, member) => current + (member.Alias + ";"));
aliases= aliases.TrimEnd(';');
return aliases;
}
private string GetDisplayNames(ObservableCollection<SelectableTeamMember> selectedStepMembers)
{
string names = selectedStepMembers.Aggregate("", (current, member) => current + (member.Name + ";"));
names= names.TrimEnd(';');
return names;
}
public void AssignResourcesToStep(ObservableCollection<SelectableTeamMember> selectedStepMembers)
{
StepTaskViewInfo.CurrentStep.StepTeamMembers = selectedStepMembers;
StepTaskViewInfo.CurrentStep.Resources = GetAliases(selectedStepMembers);
StepTaskViewInfo.CurrentStep.StepResourceDisplayName = GetDisplayNames(selectedStepMembers);
foreach (var task in StepTaskViewInfo.CurrentStep.ProjectTasks)
{
task.AllTaskTeamMembers = StepTaskViewInfo.CurrentStep.StepTeamMembers;
task.Resources = GetAliases(StepTaskViewInfo.CurrentStep.StepTeamMembers);
task.TaskResourceDisplayName = GetDisplayNames(StepTaskViewInfo.CurrentStep.StepTeamMembers);
}
}
public void AssignResourcesToTask(ObservableCollection<SelectableTeamMember> selectedTaskMembers, string taskId)
{
var task = StepTaskViewInfo.CurrentStep.ProjectTasks.First(st => st.Id == taskId);
task.Resources = GetAliases(selectedTaskMembers);
task.TaskResourceDisplayName = GetDisplayNames(selectedTaskMembers);
}
public void AssignTaskTips(string ttid)
{
string taskTip = projectWorkflowService.GetTaskTip(ttid);
foreach (var task in StepTaskViewInfo.CurrentStep.ProjectTasks)
{
if (task.TemplateTaskId == ttid)
task.TaskTip = taskTip;
}
}
#region peerreview
public void DownloadDocument(string artifactId, string fileName)
{
projectWorkflowService.DownloadActivityArtifact(artifactId, fileName);
}
public void UploadDocument(string artifactId,string file)
{
projectWorkflowService.UploadActivityArtifact(StageId, artifactId, file);
var projectDocuments = projectWorkflowService.LoadPeerReviewDocuments(StageId);
projectDocuments.Items.Add(new ActivityArtifact { FileName = string.Empty });
StepTaskViewInfo.PeerReview.Documents = projectDocuments;
}
private void GetUsers()
{
foreach (ProjectPeerReview t in StepTaskViewInfo.PeerReview.Reviews.Items.ToList())
{
if (string.IsNullOrEmpty(t.Id))
{
if (!string.IsNullOrEmpty(t.Alias))
{
User current = userService.SearchAlias(t.Alias);
if (current == null)
{
MessageBox.Show("Could not find reviewer " + t.Alias);
StepTaskViewInfo.PeerReview.Reviews.Items.Remove(t);
}
else
{
t.Name = current.Name;
}
}
}
}
}
internal User[] GetSearchingUsersName(string name)
{
return userService.Search(name);
}
#endregion
public void UpdateTaskStatus(object selectedValue)
{
foreach (var task in StepTaskViewInfo.CurrentStep.ProjectTasks)
{
task.Status = selectedValue.ToString();
}
}
public void LoadTasksForCurrentStep()
{
StepTaskViewInfo.CurrentStep.ProjectTasks = projectWorkflowService.LoadProjectTasks( StepTaskViewInfo.CurrentStep.Id);
StepTaskViewInfo.UpdateTaskResources();
}
public void SaveCurrentTasksWithStep()
{
if (StepTaskViewInfo.CurrentStep != null)
{
projectWorkflowService.SaveTasksWithStep(StageId, StepTaskViewInfo.CurrentStep, StepTaskViewInfo.CurrentStep.ProjectTasks);
}
}
public bool SaveData()
{
if (StepTaskViewInfo.CurrentStep != null)
{
GetUsers();
return projectWorkflowService.SaveStepTaskViewInfo(StepTaskViewInfo, StageId);
}
return true;
}
}
}
DataGridColumn.IsReadOnly property will make all cells in the column either ReadOnly or not ReadOnly. DataGridCell.IsReadOnly property is not assignable too. What you are trying to do is to make a column readonly if some other property is false, else column should be editable. It is better to use a template column and set the control accordingly. MSDN
For example, if I want user to edit CarNumber property value is HasCar property value is true else not, then if I write :
<DataGridTextColumn Header="CarNumber" Binding="{Binding CarNumber}" IsReadOnly="{Binding HasCar"/>
//
This will still allow all column values to be editable irrespective of HasCar value.
//
If I use Template column and use DataTrigger then I get the desired effect. This wont allow user to type/edit CarNumber if HasCar property is false :
<DataGridTemplateColumn Header="CarNumber" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="TxtCarNumber" Text="{Binding CarNumber}" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding HasCar}" Value="False">
<Setter Property="TextBox.Visibility" Value="Hidden"/>
<Setter Property="TextBox.Width" Value="100"/>
</DataTrigger>
<DataTrigger Binding="{Binding HasCar}" Value="True">
<Setter Property="TextBox.Visibility" Value="Visible"/>
<Setter Property="TextBox.Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
In your code do this, as you want your CheckBox to remain visible but not enabled :
<DataGridTemplateColumn Width ="60" HeaderStyle="{StaticResource HeaderStyle}" Header="Selected" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid IsEnabled={Binding StepTaskViewInfo.AreStepsTasksReadonly}>
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"
IsChecked="{Binding IsSelected,UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsTaskEnabled,UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Related
Goal
I am aiming to create a button that triggers selected row RowDetailsTemplate visibility.
Problem
I somewhat managed to do it, but on my button click.. it displays RowDetailsTemplates for every single record. I need it to display the selected row, not all.
Collapsed
Visible
Question
How can I only trigger the selected row visibility state?
Code
XAML
<DataGrid ItemsSource="..." SelectedItem="..." IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Product Code" Binding="{Binding ProductCode}" />
<DataGridTemplateColumn Header="Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Edit" Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.TriggerVisibility }" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Background="Orange">
<TextBlock Text="Test" />
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="DetailsVisibility" Value="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.IsVisible}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
View Model
public ICommand TriggerVisibility { get; }
private void GetVisibleCondition()
{
if(IsVisible == Visibility.Visible)
{
IsVisible = Visibility.Collapsed;
}
else if(IsVisible == Visibility.Collapsed)
{
IsVisible = Visibility.Visible;
}
}
private Visibility _isVisible = Visibility.Collapsed;
public Visibility IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
OnPropertyChanged(nameof(IsVisible));
}
}
You cannot use a single property of a single view model to toggle the visibility of each individual row.
You should add an IsDetailsVisibile property at row level, i.e. to your data object T in the IEnumerable<T> ItemsSource of the DataGrid:
public class Product : INotifyPropertyChanged
{
public Product()
{
TriggerVisibility = new RelayCommand2(() => IsDetailsVisibile = !IsDetailsVisibile);
}
public string ProductCode { get; set; }
public ICommand TriggerVisibility { get; }
private bool _isDetailsVisibile;
public bool IsDetailsVisibile
{
get { return _isDetailsVisibile; }
set { _isDetailsVisibile = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class RelayCommand : ICommand
{
private readonly Action _execute;
public RelayCommand(Action execute) => _execute = execute;
public event EventHandler CanExecuteChanged;
public bool CanExecute(Object parameter) => true;
public void Execute(Object parameter) => _execute();
}
XAML:
<DataGrid.Columns>
<DataGridTextColumn Header="Product Code" Binding="{Binding ProductCode}" />
<DataGridTemplateColumn Header="Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Edit" Command="{Binding TriggerVisibility}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Background="Orange">
<TextBlock Text="Test" />
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Style.Resources>
<Setter Property="DetailsVisibility" Value="{Binding IsDetailsVisibile,
Converter={StaticResource BooleanToVisibilityConverter}}" />
</Style>
</DataGrid.RowStyle>
I use a combobox and I would like to proceed as follows:
I choose an element in cmb1, this allows to display in cmb2 a Collection.
This code allows me to retrieve the data I want ( result A = ObservableCollectionA, result B = ObservableCollection B...)
<ComboBox Name="cmbResultatListe"
Margin="0,10,0,10"
Grid.Row="4"
Grid.Column="2"
Height="25"
Width="250" >
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Sections}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedChoiceList}" Value="Etablissement">
<Setter Property="ItemsSource" Value="{Binding Etablissements}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedChoiceList}" Value="Service">
<Setter Property="ItemsSource" Value="{Binding Services}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
I would now like to divide my combobox into three grids, so that I can proceed as follows:
If result A is selected => cmb2 grid0 = ObservableCollectionA.ID, cmb2 grid1 = observableCollectionA.Name...
If result B is selected => cmb2 grid0 = ObservableCollectionB.Name, cmb2 grid1 = observableCollectionB.Years...
And i don't know how can i do that.
Any tips ?
Thank you for your help.
Edit :
c# code :
private ObservableCollection<Etablissement> _EtablissementsUtilisateur;
public ObservableCollection<Etablissement> EtablissementsUtilisateur
{
get
{
return _EtablissementsUtilisateur;
}
set
{
if (value != _EtablissementsUtilisateur)
{
_EtablissementsUtilisateur = value;
RaisePropertyChanged(nameof(EtablissementsUtilisateur));
}
}
}
private ObservableCollection<ServiceSectionModel> _Services;
public ObservableCollection<ServiceSectionModel> Services
{
get
{
return _Services;
}
set
{
if (value != _Services)
{
_Services = value;
RaisePropertyChanged(nameof(Services));
}
}
}
private ObservableCollection<ServiceSectionModel> _Sections;
public ObservableCollection<ServiceSectionModel> Sections
{
get
{
return _Sections;
}
set
{
if (value != _Sections)
{
_Sections = value;
RaisePropertyChanged(nameof(Sections));
}
}
}
private string _SelectedChoiceList;
public string SelectedChoiceList
{
get
{
return _SelectedChoiceList;
}
set
{
if (value != _SelectedChoiceList)
{
_SelectedChoiceList = value;
RaisePropertyChanged(nameof(SelectedChoiceList));
}
}
}
Etablissements = new ObservableCollection<Etablissement>((await _dataService.GetEtablissements().ConfigureAwait(false)));
Services = await _dataService.GetServicesAsync(false).ConfigureAwait(false);
Sections = await _dataService.GetSectionsAsync(_dataService.ParamGlobaux.IDEtablissement).ConfigureAwait(false);
Etablissement contain ID, Name, Years.
Service contain Color, ID, Name.
Section contain Color, ID, SectionName.
Edit 2 : I would like something like this exemple :
<ComboBox Name="CbService" HorizontalAlignment="Left" Margin="115,67,0,0" VerticalAlignment="Top" Width="150" ItemsSource="{Binding}" SelectionChanged="CbRecherche_SelectionChanged" KeyboardNavigation.TabIndex="1" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Libelle}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="gd" TextElement.Foreground="Black" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Fill="{Binding Fond}" Grid.Column="0"/>
<TextBlock Margin="5" Grid.Column="0" Text="{Binding ID}"/>
<TextBlock Margin="5" Grid.Column="1" Text="{Binding Libelle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
Currently my combobox displays a string. I want something like this :
In this example, there is an ID, a color only in the ID part, and a Name. I can't do that with my string at the moment.
I belive you may be able to reduce the size of your codes by removing the RaisePropertyChanged event as ObservableCollections already contain the INotifyPropertyChanged interface, I have made a simple example of how to use Datatemplate to display information from ObservableCollections.
Step 1: C# codes:
namespace WpfApp1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ComboBox1.ItemsSource = Services;
Services.Add(new ServiceSectionModel { Color = Brushes.Red, ID = "Clem", Name = "Clementine" });
Services.Add(new ServiceSectionModel { Color = Brushes.White, ID = "011", Name = "Logistique" });
Services.Add(new ServiceSectionModel { Color = Brushes.Green, ID = "MBT", Name = "Montbrilland" });
}
public class ServiceSectionModel
{
public string ID { get; set; }
public string Name { get; set; }
public SolidColorBrush Color { get; set; }
}
ObservableCollection<ServiceSectionModel> Services = new ObservableCollection<ServiceSectionModel>();
}
}
Step 2: XAML codes:
<ComboBox x:Name="ComboBox1" HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Background="{Binding Color}" Text="{Binding ID}" Margin="0,0,10,0" Padding="5,2,10,2"></TextBlock>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
MVVM newbie looking for a bit of guidance about datagrid binding. I have difficulty with setting up the properties where I can see that the contents of a datagrid cell has changed. I started out simple and am trying to set up an updateable country list with currency codes. Is there a way I can bind my grid column cells to properties in the viewmodel? So far I have just introduced a property for CurrencyCode, but I never execute the setter. What am I doing wrong?
Model:
using System.Linq;
using System.Collections.ObjectModel;
namespace AdminTool.DataModel.Repositories
{
public class AccountingRepository : BaseRepository
{
private AdminToolEntities entities = new AdminToolEntities();
// Country List
public ObservableCollection<CountryData> GetCountryList()
{
var result = (from c in entities.Countries
from u in entities.Currencies.Where(u => u.ID == c.CurrencyID).DefaultIfEmpty()
select new CountryData { IsChanged = false,
ID = c.ID,
CountryName = c.CountryName,
CountryCodeISO = c.CountryCodeISO,
CurrencyCode = u.CurrencyCode})
.OrderBy(o => o.CountryName)
.ToList();
return new ObservableCollection<CountryData>(result);
}
public void SaveCountryList(ObservableCollection<CountryData> countryList)
{
var result = (from l in countryList
from c in entities.Countries.Where(c => c.ID == l.ID)
from u in entities.Currencies.Where(u => u.CurrencyCode == l.CurrencyCode).DefaultIfEmpty()
where l.IsChanged
select l)
.ToList();
foreach (CountryData cd in result)
{
Country c = (Country)entities.Countries.Where(l => l.ID == cd.ID).FirstOrDefault();
if (c == null) // new entry
{
c = new Country();
entities.Countries.Add(c);
}
c.CountryName = cd.CountryName;
c.CountryCodeISO = cd.CountryCodeISO;
c.Currency = entities.Currencies.Where(u => u.CurrencyCode == cd.CurrencyCode).FirstOrDefault();
}
entities.SaveChanges();
}
}
/// <summary>
/// Data structures
/// </summary>
public class CountryData
{
public int ID { get; set; }
public string CountryName { get; set; }
public string CountryCodeISO { get; set; }
public string CurrencyCode { get; set; }
public bool IsChanged { get; set; }
}
}
View:
<Window x:Class="AdminTool.Desktop.View.CountryList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:AdminTool.Desktop.ViewModel"
Title="AdminTool" Height="600" Width="500">
<Window.DataContext>
<vm:AccountingViewModel/>
</Window.DataContext>
<Grid>
<DataGrid Name="dgCountryList"
ItemsSource="{Binding CountryList, Mode=TwoWay}"
SelectedItem="{Binding Path=SelectedCountry, Mode=TwoWay}"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="450" Width="450" Margin="20,20,0,0"
AutoGenerateColumns="False"
SelectionMode="Single"
SelectionUnit="FullRow"
GridLinesVisibility="All"
CanUserDeleteRows="True"
CanUserAddRows="True">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChanged}" Value="true" >
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<!--<DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="50" />-->
<DataGridTextColumn Header="Country" Binding="{Binding Path=CountryName, Mode=TwoWay}" Width="250" />
<DataGridTextColumn Header="ISO Code" Binding="{Binding Path=CountryCodeISO, Mode=TwoWay}" Width="80" />
<DataGridTextColumn Header="Currency" Binding="{Binding Path=CurrencyCode, Mode=TwoWay}" Width="80" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Save" Command="{Binding Path=SaveCommand}" IsEnabled="{Binding Path=UpdateButtonEnabled}" HorizontalAlignment="Left" Margin="223,512,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Cancel" Command="{Binding Path=CancelCommand}" HorizontalAlignment="Left" Margin="343,512,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
ViewModel:
using System.Collections.ObjectModel;
using AdminTool.DataModel.Repositories;
using AdminTool.Helpers;
namespace AdminTool.Desktop.ViewModel
{
public class AccountingViewModel : ViewModelBase
{
private ObservableCollection<CountryData> _countryList;
public ObservableCollection<CountryData> CountryList
{
get { return _countryList; }
set
{
_countryList = value;
RaisePropertyChanged("CountryList");
}
}
private CountryData _selectedCountry;
public CountryData SelectedCountry
{
get { return _selectedCountry; }
set
{
_selectedCountry = value;
RaisePropertyChanged("SelectedCountry");
}
}
private string currencyCode;
public string CurrencyCode
{
get { return currencyCode; }
set {
currencyCode = value;
SelectedCountry.IsChanged = true;
}
}
private bool updateButtonEnabled = false;
public bool UpdateButtonEnabled
{
get { return updateButtonEnabled; }
set { updateButtonEnabled = value;
RaisePropertyChanged("UpdateButtonEnabled");
}
}
#region Commands -----------------------------------------------------------------------------------
public RelayCommand SaveCommand { get; set; }
public RelayCommand CancelCommand { get; set; }
#endregion
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public AccountingViewModel()
{
SaveCommand = new RelayCommand(Save);
CancelCommand = new RelayCommand(Cancel);
AccountingRepository repo = new AccountingRepository();
CountryList = repo.GetCountryList();
}
#region Public methods -----------------------------------------------------------------------------
void Save(object parameter)
{
AccountingRepository repo = new AccountingRepository();
repo.SaveCountryList(CountryList);
}
void Cancel(object parameter)
{
}
#endregion
}
}
Thanks in advance for any help.
you have to handle the property changed event:
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
You need to make changes in model. Implement notify interface as given below.
Model - With name property being updated
public class CountryData : INotifyPropertyChanged
{
private string countryName;
public string CountryName
{
get { return countryName; }
set
{
countryName = value;
RaisePropertyChanged("CountryName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
View - Use UpdateSourceTrigger=PropertyChanged with Bindings in grid.
<Grid>
<DataGrid Name="dgCountryList"
ItemsSource="{Binding CountryList, Mode=TwoWay}"
SelectedItem="{Binding Path=SelectedCountry, Mode=TwoWay}"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="450" Width="450" Margin="20,20,0,0"
AutoGenerateColumns="False"
SelectionMode="Single"
SelectionUnit="FullRow"
GridLinesVisibility="All"
CanUserDeleteRows="True"
CanUserAddRows="True">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChanged}" Value="true" >
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<!--<DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="50" />-->
<DataGridTextColumn Header="Country" Binding="{Binding Path=CountryName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="250" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Save" Command="{Binding Path=SaveCommand}" IsEnabled="{Binding Path=UpdateButtonEnabled}" HorizontalAlignment="Left" Margin="223,512,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Cancel" Command="{Binding Path=CancelCommand}" HorizontalAlignment="Left" Margin="343,512,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
I have this WPF structure:
<UserControl
xmlns:viewModel="clr-namespace:..ViewModel.ToneAudiogramLegend">
...
<DataTemplate DataType="{x:Type viewModel:ToneAudiogramLegendTableViewModel}">
...
<DataGrid Grid.Row="1" Grid.Column="0" ItemsSource="{Binding ToneAudiogramLegneds}" HeadersVisibility="None" AutoGenerateColumns="False" IsReadOnly="True" BorderBrush="Transparent" BorderThickness="0"
MinWidth="100" Height="{Binding Height, Mode=OneWay}" KeyboardNavigation.DirectionalNavigation="None" Grid.ColumnSpan="6" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" GridLinesVisibility="None" ColumnWidth="*"
Margin="1" wpfmvvm:DataGridRowHeightBehaviour.AutoFitRowHeight="True" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
<DataGrid.Columns>
<DataGridTemplateColumn HeaderTemplate="{x:Null}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label x:Name="PART_Content" Content="{Binding Path=Left.Content}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="Gold"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsMonochrome}" Value="True">
<Setter TargetName="PART_Content" Property="Foreground" Value="Green"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
<DataTemplate>
The binding of the Value works because the color gets the default value of Left.Color of <Setter Property="Foreground" Value="{Binding Path=Left.Color}"/>
The IsMonochrome property changes value when a specific event occurs, but the Foreground color does not change to green. I am not quite sure the format and the structure is appropriate. I am not sure if <DataTemplate> is a problem, since I have another <DataTemplate> in higher order
The classes:
public partial class ToneAudiogramLegendTableViewModel : ViewModelBase, IToneAudiogramLegendTableViewModel, IHandleMonochromeReportElement
{
public bool IsMonochrome
{
get { return GetValue<bool>("IsMonochrome"); }
private set { SetValue("IsMonochrome", value); }
}
public void SwitchToMonochromeMode()
{
IsMonochrome = true;
}
public void SwitchToColorMode()
{
IsMonochrome = false;
}
}
and
public class ToneAudiogramLegendViewModel : ViewModelBase, IToneAudiogramLegendVM
{
public string Name
{
get { return GetValue<string>("Name"); }
set { SetValue("Name", value); }
}
public LegendViewModel Left
{
get { return GetValue<LegendViewModel>("Left"); }
set { SetValue("Left", value); }
}
}
and
public class LegendViewModel : ViewModelBase
{
public object Content
{
get { return GetValue<object>("Content"); }
set { SetValue("Content", value); }
}
public Brush Color
{
get { return GetValue<Brush>("Color"); }
set { SetValue("Color", value); }
}
public LegendViewModel(object content, Brush color)
{
Content = content;
Color = color;
}
}
What might be the issue?
In your CellTemplate you are binding to ToneAudiogramLegendViewModel object, where are properties Name and Left. The IsMonochrome is on different object, that's why it does not work.
You need to either define IsMonochrome property or you need create property to reference ToneAudiogramLegendTableViewModel in ToneAudiogramLegendViewModel to be able to databind to IsMonochrome.
EDIT:
based on your comments:
<DataTrigger Binding="{Binding Path=DataContext.IsMonochrome,
RelativeSource={RelativeSource DataGrid}}"
Value="True">
I have a listview in my WPF application and the first column is a Checkbox. This checkbox is bound to the IsSelected property of my model and the event propogation happens correctly.
I also have a Checkbox in the same column's header and want to implement a 'Select All' feature where it checks all the listview items.
I'm using pattern MVVM.
The Event doesn't fire!
Can someone explain what I am doing wrong here..
The relevant code portions are mentioned below..
XAML:
<ListView Grid.Row="0"
ItemsSource="{Binding Path=WorkOrders}"
Margin="5,10,5,5"
Name="WorkOrders"
SelectionMode="Multiple"
FontSize="13"
Background="AliceBlue"
BorderBrush="AliceBlue">
<!--Style of items-->
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<!--Properties-->
<Setter Property="Control.HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Control.VerticalContentAlignment" Value="Center" />
<!--Trigger-->
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView >
<GridViewColumn CellTemplate="{StaticResource CheckBoxDataTemplate}" Width="80" >
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Command="{Binding Path=SelectAllCommand}" />
</DataTemplate>
</GridViewColumn.HeaderTemplate>
</GridViewColumn>
<GridViewColumn Header="WorkOrder" CellTemplate="{StaticResource DetailIdenTemplate}" Width="300"/>
</GridView>
</ListView.View>
</ListView>
Model:
public class WorkOrder
{
public int CD_WORK_ORDER { get; set; }
public string ID_WORK_ORDER { get; set; }
public bool IsSelected { get; set; }
}
ViewModel:
public class LockWorkOrderSelectionViewModel : ViewModelBase
{
RelayCommand _selectAllCommand;
public ICommand SelectAllCommand
{
get
{
if (_selectAllCommand == null)
{
_selectAllCommand = new RelayCommand(
param => SelectAllElement(),
param => CanSelectAll);
}
//RaiseEvent(new RoutedEventArgs(SearchEvent));
return _selectAllCommand;
}
}
private bool _selectedAllElement;
public bool SelectAllElement()
{
foreach (var item in WorkOrders)
{
item.IsSelected = true;
}
return true;
}
public bool CanSelectAll
{
get { return true; }
}
public List<string> WorkOrdersList
{
get { return _workOrdersList; }
}
private ObservableCollection<WorkOrder> _workOrders = new ObservableCollection<WorkOrder>();
public ObservableCollection<WorkOrder> WorkOrders
{
get
{
int progr = 1;
foreach (var item in WorkOrdersList)
{
if (_workOrders.FirstOrDefault(i => i.ID_WORK_ORDER == item) == null)
{
_workOrders.Add(new WorkOrder { CD_WORK_ORDER = progr, ID_WORK_ORDER = item, IsSelected = false });
progr++;
}
}
return _workOrders;
}
}
}
<CheckBox IsChecked="{Binding DataContext.SelectAll, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
Works for me.