Binding ObservableCollection<T> ItemSource to DataGrid - c#

In my wpf application, I am using Prism library to autowire my viewmodel into my view. I have this working for simple properties which automatically binds view and model. Now I am trying to bind an ObservableCollection<T> data to bind into DataGrid with no luck. Below is my structure for current scenario.
ConfigurationDetails.cs
public class ConfigurationDetails:BindableBase
{
private int _id;
public int Id { get { return _id; } set { SetProperty(ref _id, value); } }
private string _configFName;
private string _configSName;
private string _configUName;
public string ConfigFName { get { return _configFName; } set { SetProperty(ref _configFName, value); } }
public string ConfigSName { get { return _configSName; } set { SetProperty(ref _configSName, value); } }
public string ConfigUName { get { return _configUName; } set { SetProperty(ref _configUName, value); } }
}
ConfigurationWindowViewModel.cs
public class ConfigurationWindowViewModel : BindableBase
{
public ConfigurationWindowViewModel()
{
ConfigDetails = new ObservableCollection<ConfigurationDetails>();
}
private ObservableCollection<ConfigurationDetails> _configDetails;
public ObservableCollection<ConfigurationDetails> ConfigDetails { get { return _configDetails; } set { SetProperty(ref _configDetails, value); } }
}
ConfigurationWindow.xaml
<UserControl x:Class="MyApp.Views.ConfigurationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
HorizontalContentAlignment="Center">
....
<DataGrid ItemsSource="{Binding ConfigDetails}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="F NAME" Width="*" Binding="{Binding Path=ConfigFName}"/>
<DataGridTextColumn Header="S NAME" Width="*" Binding="{Binding Path=ConfigSName}"/>
<DataGridTextColumn Header="U NAME" Width="*" Binding="{Binding Path=ConfigUName}"/>
</DataGrid.Columns>
</DataGrid>
....
</UserControl>
ConfigurationWindow.xaml.cs
public ConfigurationWindow()
{
InitializeComponent();
using (_db = new MyEntities())
{
var configRecords = _db.tblConfigs.ToList().Select(x => new ConfigurationDetails()
{
ConfigFName = x.ConfigFName,
ConfigSName = x.ConfigSName,
ConfigUName = x.ConfigUName,
Id = x.ConfigID
});
model.ConfigDetails = new ObservableCollection<ConfigurationDetails>(configRecords);
//model.ConfigDetails will have records assigned to it when debugged
}
}
}
But still, I don't see any records displayed in my DataGrid. What is it am missing here. I have also used ViewModelLocator.AutoWireViewModel and it has been working flawlessly for other model properties. Hope to get some help here.
Update - I have my ConfigurationWindowViewModel.cs placed in ViewModels folder. Sorry for missing that to mention.

Where are you adding the ConfigurationDetails objects to the ConfigDetails collection? Try to add some items in the constructor of the ConfigurationWindowViewModel class:
public class ConfigurationWindowViewModel : BindableBase
{
public ConfigurationWindowViewModel()
{
ConfigDetails = new ObservableCollection<ConfigurationDetails>();
//add some items...:
ConfigDetails.Add(new ConfigurationDetails() { ConfigFName = "F", ConfigSName = "S", ConfigUName = "U" });
ConfigDetails.Add(new ConfigurationDetails() { ConfigFName = "F", ConfigSName = "S", ConfigUName = "U" });
ConfigDetails.Add(new ConfigurationDetails() { ConfigFName = "F", ConfigSName = "S", ConfigUName = "U" });
}
private ObservableCollection<ConfigurationDetails> _configDetails;
public ObservableCollection<ConfigurationDetails> ConfigDetails { get { return _configDetails; } set { SetProperty(ref _configDetails, value); } }
}
And for the autowire functionality in Prism to work out-of-the-box you need to follow the default naming conventions, i.e. your ConfigurationWindowViewModel class should be located in a "ViewModels" folders (and in the .ViewModels namespace) in the root folder of your project. Please refer to Brian Lagunas' blog post for more information about this and how you could change the default conventions by calling the ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver method in your Bootstrapper or App.xaml.cs: http://brianlagunas.com/getting-started-prisms-new-viewmodellocator/
Also make sure that your implements the IView interface:
public partial class ConfigurationWindow() : Window, IView
{
...
}

Well... First of all ObservableCollection implements INotifyPropertyChanged, so you don't have to do that.
Then this is enough:
public ObservableCollection<ConfigurationDetails> ConfigDetails { get; set; }
public ConfigurationWindow()
{
InitializeComponent();
using (_db = new MyEntities())
{
var configRecords = _db.tblConfigs.ToList().Select(x => new ConfigurationDetails()
{
ConfigFName = x.ConfigFName,
ConfigSName = x.ConfigSName,
ConfigUName = x.ConfigUName,
Id = x.ConfigID
});
foreach(var config in configRecords)
{
model.ConfigDetails.Add(config);
}
}
}
}

Related

complexed Issue with hiding ListView Xamarin Forms

I have a search bar with the property Text binded to a string property in my ViewModel.
I also have Behaviors within the search bar so that every time the text is changed a search is done within a list of objects using NewTextValue passed to as the query string.
The issue I have is that, I make the ListView invisible until a non-empty string is passed to my Search/Filter command (obviously.. :)). I have tried to enforcing hiding the ListView for a couple scenarios e.g. if all text is removed from the search bar.
When an item is selected from the now visible list view I used that item to populate the Text property of my SearchBar, after which I cannot hide it within code. All attempts have failed and the ListView remains visible. Note: I explicity created a hide button separately and saw it worked so I am wondering if I cannot tie hiding the view with setting the searchbar Text property.
View
<SearchBar Text="{Binding SearchText}">
<SearchBar.Behaviors>
<prismBehaviors:EventToCommandBehavior EventName="TextChanged"
Command="{Binding FilterOccupationsListCommand}"
EventArgsParameterPath="NewTextValue"/>
</SearchBar.Behaviors>
</SearchBar>
<ListView ItemsSource="{Binding FilteredOccupations}" IsVisible="{Binding FilteredOccupationsVisible}" SelectedItem="{Binding Occupation, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Please Note : My ViewModel inherits from BaseViewModel which inherits INotifyPropertyChanged. SetProperty() is what notifies the property. This is quite common with MvvmCross, Prism etc.
ViewModel
public class MyViewModel : BaseViewModel
{
public DelegateCommand<string> FilterOccupationsListCommand { get; }
public MyViewModel()
{
FilterOccupationsListCommand = new DelegateCommand<string>(FilterOccupationsList);
}
private void FilterOccupationsList(string query)
{
if (!string.IsNullOrWhiteSpace(query))
{
FilteredOccupationsVisible = true;
var searchResult = Occupations.Where(x => x.Name.ToLower().Contains(query));
FilteredOccupations = new ObservableCollection<Occupation>(searchResult);
}
else
FilteredOccupationsVisible = false;
}
private Occupation _occupation;
public Occupation Occupation
{
get => _occupation;
set
{
SetProperty(ref _occupation, value);
SearchText = value.Name;
}
}
private string _name;
public string Name { get => _name; set => SetProperty(ref _name, value); }
private string _searchText;
public string SearchText
{
get => _searchText;
set {
SetProperty(ref _searchText, value);
FilteredOccupationsVisible = false;
}
}
private bool _filteredOccupationsVisible;
public bool FilteredOccupationsVisible { get => _filteredOccupationsVisible; set => SetProperty(ref _filteredOccupationsVisible, value); }
public ObservableCollection<Occupation> _filteredOccupations = new ObservableCollection<Occupation>();
public ObservableCollection<Occupation> FilteredOccupations { get => _filteredOccupations; set { SetProperty(ref _filteredOccupations, value); } }
}
If not using Behaviors in SearchBar , you can have a try with TextChanged method of itself.
<SearchBar x:Name="MySearchBar" Text="SearchText" TextChanged="SearchBar_TextChanged" />
In ContentPage , when text cheanged fire here :
MyViewModel myViewModel = new MyViewModel();
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
Console.WriteLine("new -- " + e.NewTextValue + "-- old -- " + e.OldTextValue);
Console.WriteLine("MyEntry --" + MySearchBar.Text);
//Here can invoke FilterOccupationsList of MyViewModel
myViewModel.FilterOccupationsList(MySearchBar.Text);
}
Else if using Command to do , you need to add isntance of ICommand in MyViewModel to invoke FilterOccupationsList.
public class MyViewModel : BaseViewModel
{
public ICommand FilterOccupationsListCommand { private set; get; }
...
public MyViewModel()
{
FilterOccupationsListCommand = new Command<string>((NewTextValue) =>
{
// Pass value to FilterOccupationsList.
Console.WriteLine("SearchBar new text --" + NewTextValue);
FilterOccupationsList(NewTextValue);
});
}
...
}

WPF DataGrid: how to know if ValidationTemplate is enabled

In a WPF application, I have a view with an editable datagrid and a viewmodel. This is an existing codebase, and a problem arises: there are fields in the viewmodel that raises exceptions but those fields are not in the view. A refactor is necessary, but for now we need to implement a visual clue (red border around the row) for the user as a quick fix.
From the viewmodel, I raise an event that a validation took place, and in the code-behind, I want to check in the datagridrow if the ValidationErrorTemplate is enabled.
As the elements added by the ValidationErrorTemplate are added as AdornerLayer outside of the datagridrows, it seems that I have no clue to which datagridrow this is coupled?
I have not much code to show, just that I get to the correct datagridrow for which viewmodel a validation took place:
private void OnValidationEvent(ValidationEventArgs e)
{
var rows = BoekingDatagrid.GetDataGridRow(e.ID);
if (e.HasErrors)
{
if (errorBorder == null)
{
row.BorderBrush = new SolidColorBrush(Colors.Red);
row.BorderThickness = new Thickness(1);
var vm = row.DataContext as ItemBaseViewModel;
LogValidationErrors(vm, UserContext);
}
}
else
{
row.BorderThickness = new Thickness(0);
}
}
Every column has the following xaml, with a Validation.ErrorTemplate:
<DataGridTemplateColumn Header="Name"
CanUserResize="False"
SortMemberPath ="Name"
Width="130"
MinWidth="130">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding ViewMode, Converter={StaticResource ViewModeToBoolean}, ConverterParameter=name}"
Validation.ErrorTemplate="{StaticResource ResourceKey=ErrorTemplate2_Grid}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
ErrorTemplate2_Grid adds a red border and tooltip to the cell.
In Visual Live Tree, you can see that the rows and the error visuals, but they are not nested:
The question is: how can I find out if there are visual error elements added to the datagridrow, when the viewmodel is invalid?
Not sure what BindingGroup exactly does, but the databound rows does have there own BindingGroups with only the databindings of the controls in the row. And a very convenient property: HasValidationError
private void OnValidationEvent(ValidationEventArgs e)
{
var row = BoekingDatagrid.GetDataGridRow();
if (row != null)
{
if (e.HasErrors)
{
if (!row.BindingGroup.HasValidationError)
{
row.BorderBrush = new SolidColorBrush(Colors.Red);
row.BorderThickness = new Thickness(1);
}
else
{
row.BorderThickness = new Thickness(0);
}
}
else
{
row.BorderThickness = new Thickness(0);
}
}
}
Answered for mine and others future reference.
Validation in WPF is typically done using the interface INotifyDataErrorInfo. There are other ways too, but the ViewModel raising an event that the View handles isn't typically one of them. A very simplified model class could look like this:
public class Model : INotifyDataErrorInfo
{
public Model(int myInt, string myString)
{
MyInt = myInt;
MyString = myString;
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors
{
get
{
try
{
return MyInt == 88 || MyString == "foo";
}
catch (Exception)
{
return true;
}
}
}
public int MyInt
{
get { throw new NotImplementedException(); }
set { }
}
public string MyString { get; set; }
public IEnumerable GetErrors(string propertyName)
{
try
{
if (propertyName == nameof(MyInt) && MyInt == 88)
return new string[] { "MyInt must not be 88" };
if (propertyName == nameof(MyString) && MyString == "foo")
return new string[] { "MyString must not be 'foo'" };
return new string[0];
}
catch (Exception)
{
return new string[] { "Exception" };
}
}
}
A quick and diry window could look like this:
public partial class MainWindow : Window
{
public MainWindow()
{
Models = new List<Model>
{
new Model(1,"hello"),
new Model(1,"foo"),
new Model(88,"hello"),
new Model(2,"world"),
};
DataContext = this;
InitializeComponent();
}
public List<Model> Models { get; set; }
}
whereas the XAML just contains <DataGrid ItemsSource="{Binding Models}" />
A red rectangle in case of an error is default. Just apply your custom template and do not set any ValidationErrorTemplate.

Trying to Filter a bound ObservableCollection to a combobox based on another ComboBox Value not working

I see several other posts about this but I cannot seem to understand exactly how to get this working properly for my usage.
Here is what I have in a nutshell.
I have two Comboboxes--Role and Position.
I have both of these bound to an ObservableCollection which has Enum Values Converted to strings loaded into it on instantiation.
<ComboBox x:Name="empRoleCB" ItemsSource="{Binding Role}" SelectedItem="{Binding RoleStr}"/>
<ComboBox x:Name="empPositionCB" ItemsSource="{Binding Pos}" SelectedItem="{Binding PosStr}"/>
In my ViewModel:
public abstract class EmployeeMenuVMBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if(!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
}
class EmployeeMenuVM : EmployeeMenuVMBase
{
private ObservableCollection<string> _pos = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Positions)));
private ObservableCollection<string> _role = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Roles)));
public ObservableCollection<string> Pos { get => _pos; }
public ObservableCollection<string> Role { get => _role; }
public string RoleStr
{
get => _roleStr;
set => SetProperty(ref _roleStr, value);
}
public string PosStr
{
get => _posStr;
set => SetProperty(ref _posStr, value);
}
}
What I want to happen is when a Role is selected, based on that selection, only certain Positions should be shown. For instance if I select "Customer Service" as a Role, then Position should only contain "Manager", "CSS" and "None". If Role is "Admin" then Position should only contain "None", and so on and so forth.
The struggle I have is how to filter this properly. I see something with using CollectionViewSource but I am unsure how to get this to work with my example.
I have 5 roles and each role will have a different list of positions that need to be shown.
What is the best way to make this work with MINIMAL extra code or XAML?
One of the things I really dislike about WPF is seemingly simple things need huge amounts of code to make them work properly many times.
First, if you think that WPF is complicated. So, you are using it wrongly.
I suggest you to use the Filter of CollectionViewSource as flow:
<ComboBox x:Name="empPositionCB" ItemsSource="{Binding MyPositionFilter}" SelectionChanged="RoleComboBox_SelectionChanged" ....../>
public ICollectionView MyPositionFilter { get; set; }
//ctor
public MyUserControlOrWindow()
{
//Before InitComponent()
this.MyPositionFilter = new CollectionViewSource { Source = MyPosObservableCollection }.View;
InitComponent();
}
public void RoleComboBox_SelectionChanged(object sender,EventArgs e)
{
//Get the selected Role (the ? is to prevent NullException (VS 2015 >))
Role r = empRoleCB.SelectedItem as Role;
//Apply the filter
this.MyPositionFilter.Filter = item =>
{
//Make you sure to convert correcteley your Enumeration, I used it here like a class
Position p = item as Position;
//Put your condition here. For example:
return r.ToLowers().Contains(p.ToLower());
//Or
return (r != null && r.Length >= p.Length);
};
}
The filter does not change your Collection, All hidden item stay in your ObservableCollection.
This can all be done in your ViewModel by changing the value of the Positions (Pos) observable collection when the role changes.
class EmployeeMenuVM : EmployeeMenuVMBase
{
public EmployeeMenuVM()
{
var emptyPositions = new List<Global.Positions>()
{ Global.Positions.None };
_rolePositions.Add(Global.Roles.None, emptyPositions);
var customerServicePositions = new List<Global.Positions>()
{ Global.Positions.None, Global.Positions.CSS, Global.Positions.Manager };
_rolePositions.Add(Global.Roles.CustomerService, customerServicePositions);
}
private Dictionary<Global.Roles, List<Global.Positions>> _rolePositions = new Dictionary<Global.Roles, List<Global.Positions>>();
private string _roleStr;
private string _posStr;
private ObservableCollection<string> _pos = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Positions)));
private ObservableCollection<string> _role = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Roles)));
public ObservableCollection<string> Pos
{
get => _pos;
set
{
SetProperty(ref _pos, value);
}
}
public ObservableCollection<string> Role
{
get => _role;
}
public string RoleStr
{
get => _roleStr;
set
{
if (SetProperty(ref _roleStr, value))
{
Global.Roles role = (Global.Roles)Enum.Parse(typeof(Global.Roles), value);
var positions = _rolePositions[role].Select(p => p.ToString());
Pos = new ObservableCollection<string>(positions);
}
}
}
public string PosStr
{
get => _posStr;
set => SetProperty(ref _posStr, value);
}
}
Here is a working tester code just to see the main idea of how to do the filtering:
MainWindow.xaml
<Window x:Class="WpfApplication3.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
x:Name="ThisView"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="600">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Roles, ElementName=ThisView}"
SelectedItem="{Binding Path=SelectedRole, ElementName=ThisView}"
Width="300" Height="60"/>
<ComboBox ItemsSource="{Binding Path=PositionCollectionView, ElementName=ThisView}" Width="300" Height="60"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public ICollectionView PositionCollectionView { get; set; }
public ObservableCollection<string> Roles { get; set; } = new ObservableCollection<string>();
public ObservableCollection<string> Positions { get; set; } = new ObservableCollection<string>();
private string _selectedRole = String.Empty;
public string SelectedRole
{
get { return _selectedRole; }
set
{
_selectedRole = value;
OnPropertyChanged();
//This Refresh activates the Filter again, so that every time you select a role, this property will call it.
PositionCollectionView.Refresh();
}
}
public MainWindow()
{
PositionCollectionView = CollectionViewSource.GetDefaultView(Positions);
PositionCollectionView.Filter = PositionsFilter;
//use you enums here
Roles.Add("Role1");
Roles.Add("Role2");
Roles.Add("Role3");
Roles.Add("Role4");
Positions.Add("Position1");
Positions.Add("Position2");
Positions.Add("Position3");
Positions.Add("Position4");
InitializeComponent();
}
private bool PositionsFilter(object position)
{
bool result = true;
//place your code according to the Role selected to decide wheather "position" should be in the position list or not
return result;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Hope it helps..

Using C#/XAML, how do I update the ItemsSourceList of a second combobox based on the selected item of the first combobox?

I have tried numerous approaches based on many examples on StackOverflow and elsewhere, but just can't get this to work. I want the list of choices for ComboBox B to be different based on the selected item in ComboBox A. Could someone please help me determine what I'm doing wrong? (I am using MVVMLight.)
Here are the ItemsSources for the two comboboxes:
private ObservableCollection<SetupSF> _sourceForSFList = new ObservableCollection<SetupSF>();
public ObservableCollection<SetupSF> SourceForSFList
{
get { return _sourceForSFList; }
set
{
_sourceForSFList = value;
}
}
private ObservableCollection<SetupSFE> _sourceForSFEList = new ObservableCollection<SetupSFE>();
public ObservableCollection<SetupSFE> SourceForSFEList
{
get { return _sourceForSFEList; }
set
{
_sourceForSFEList = value;
}
}
Here is the selector. I tried using RelayPropertyChanged even though it's an observable collection, but that had no effect. I've verified that contents of the list is updating by setting a breakpoint. The list updates, but apparently does not notify the second combobox. _selectedSF.OwnedSFEs is itself an ObservalbleCollection.
public SetupSubfactor SelectedSF
{
get { return _selectedSF; }
set
{
_selectedSF = value;
if (_selectedSF != null)
{
SourceForSFEList = _selectedSF.OwnedSFEs;
}
}
}
Here is the code for the SF type. It is defined outside the ViewModel. (Could that be the problem?
public class SetupSF : ViewModelBase
{
private string _iD;
public string Id
{
get { return _iD; }
set
{
_iD = value;
RaisePropertyChanged(nameof(Id));
}
}
private string _sfName;
public string SFName
{
get { return _sfName; }
set
{
_sfName = value;
RaisePropertyChanged(nameof(SFName));
}
}
public ObservableCollection<SetupSFE> OwnedSFEs { get; set; }
}
Here is the XAML for the comboboxes:
<ComboBox x:Name="ComboBox A"
Width="350"
ItemsSource="{x:Bind ViewModel.SourceForSFList}"
SelectedItem="{x:Bind ViewModel.SelectedSF, Mode=TwoWay}"
DisplayMemberPath="SFName"/>
<ComboBox x:Name="Comobox B"
Width="350"
ItemsSource="{x:Bind ViewModel.SourceForSFEList}"
SelectedItem="{x:Bind ViewModel.SelectedSFE, Mode=TwoWay}
DisplayMemberPath="SFEName"/>
I believe you want to change your SourceForSFEList to look like this:
private ObservableCollection<SetupSFE> _sourceForSFEList = new ObservableCollection<SetupSFE>();
public ObservableCollection<SetupSFE> SourceForSFEList
{
get { return _sourceForSFEList; }
set
{
_sourceForSFEList.Clear();
if (value != null)
{
foreach (var item in value)
{
_sourceForSFEList.Add(item);
}
}
}
}
The Binding system is calling your "get" once, and is getting the initial Collection that _sourceForSFEList points to. Later, you are changing _sourceForSFEList to point to another collection, but the Binding system doesn't know anything about that collection, and so the UI doesn't update. Once the binding system binds a UIElement to _sourceForSFEList, you need to update that original collection, instead of changing the value of _sourceForSFEList to point to a new collection.
I've tried to infer some of your original classes. Here is a working example that I used to have one CombBox's selection update another's.
public class ViewModel
{
private ObservableCollection<SetupSF> _sourceForSFList = new ObservableCollection<SetupSF>();
public ViewModel()
{
_sourceForSFList = new ObservableCollection<SetupSF>()
{
new SetupSF() { SFName = "One", OwnedSFEs = new ObservableCollection<SetupSFE> () { new SetupSFE() { SFEName = "Three" }, new SetupSFE() { SFEName = "Four" } } },
new SetupSF() { SFName = "Two", OwnedSFEs = new ObservableCollection<SetupSFE> () { new SetupSFE() { SFEName = "Five" }, new SetupSFE() { SFEName = "Six" } } }
};
_sourceForSFEList = new ObservableCollection<SetupSFE>()
{
new SetupSFE() { SFEName = "One"},
new SetupSFE() { SFEName = "Two"}
};
}
private SetupSF _selectedSF;
public SetupSF SelectedSF
{
get { return _selectedSF; }
set
{
_selectedSF = value;
if (_selectedSF != null)
{
SourceForSFEList = _selectedSF.OwnedSFEs;
}
}
}
public ObservableCollection<SetupSF> SourceForSFList
{
get { return _sourceForSFList; }
set
{
_sourceForSFList = value;
}
}
private ObservableCollection<SetupSFE> _sourceForSFEList = new ObservableCollection<SetupSFE>();
public ObservableCollection<SetupSFE> SourceForSFEList
{
get { return _sourceForSFEList; }
set
{
_sourceForSFEList.Clear();
if (value != null)
{
foreach (var item in value)
{
_sourceForSFEList.Add(item);
}
}
}
}
}
public class SetupSF
{
public String SFName { get; set; }
public ObservableCollection<SetupSFE> OwnedSFEs;
}
public class SetupSFE
{
public String SFEName { get; set; }
}
<Page
x:Class="App7.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App7"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<local:ViewModel />
</Page.DataContext>
<StackPanel Orientation="Horizontal">
<ComboBox x:Name="ComboBoxA"
Width="350"
ItemsSource="{Binding SourceForSFList}"
SelectedItem="{Binding SelectedSF, Mode=TwoWay}"
DisplayMemberPath="SFName" Margin="10"/>
<ComboBox x:Name="ComoboxB"
Width="350"
ItemsSource="{Binding SourceForSFEList}"
SelectedItem="{Binding SelectedSFE, Mode=TwoWay}"
DisplayMemberPath="SFEName" Margin="10"/>
</StackPanel>
</Page>

Translate DataGridComboBoxColumn on the fly

I have got a DataGridComboBoxColumn that I need to translate with the WPFLocalizationExtension.
I have got a static method in my view model, that provides the available values:
public static List<ProfileSegmentType> AvailableSegmentTypes
{
get
{
var availableSegmentTypes = new List<ProfileSegmentType>
{
new ProfileSegmentType { Value = ProfileSegmentTypeEnum.Arc },
new ProfileSegmentType { Value = ProfileSegmentTypeEnum.Line },
};
return availableSegmentTypes;
}
}
The ProfileSegmentType looks like this:
public class ProfileSegmentType
{
public ProfileSegmentTypeEnum Value { get; set; }
public string Label
{
get { return Resources.Localization.ADummyValue; }
}
}
My data grid column definition looks like this:
<DataGridComboBoxColumn Header="..."
ItemsSource="{Binding Source={x:Static viewModels:ProfileGeometryViewModel.AvailableSegmentTypes}}"
SelectedValueBinding="{Binding SegmentType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Value"
DisplayMemberPath="Label" />
Now I get labels translated (right now the dummy value). But when I switch the language, the cells are not updated, of course. How can I achieve is, that the cell contents are updated, when I switch the language? (I also tried to use a value converter, but couldn't get it to work this way)
With the help of your comments, I could solve it. I had to change my ProfileSegmentType like so:
public class ProfileSegmentType : INotifyPropertyChanged
{
public ProfileSegmentType()
{
LocalizeDictionary.Instance.PropertyChanged += (e, a) =>
{
if (a.PropertyName == "Culture")
PropertyChanged(this, new PropertyChangedEventArgs("Label"));
};
}
public ProfileSegmentTypeEnum Value { get; set; }
public string Label
{
get { return Common.Resources.Localization.Add; }
}
public event PropertyChangedEventHandler PropertyChanged = (e, a) => {};
}

Categories