WPF/MVVM: Property Declaration Requiring Supporting Method/Function Logic - wpf

I have two properties I'm setting for two WPF combo boxes: one for Month and one for Day. The MonthIndex property looks like this:
private int monthIndex = DateTime.Today.Month - 1;
public int MonthIndex
{
get { return monthIndex; }
set
{
if (monthIndex != value)
{
monthIndex = value;
RaisePropertyChanged("MonthIndex");
}
}
}
I need to set the DayIndex property but unlike the Month property, it requires calculation - can't use simple declaration like
private int _dayIndex = DateTime.Today.Day - 1;
Each calendar day can have 0 or more events. e.g., if 6/30 had three events, such events would be stored as 30, 30.1, and 30.2 (in ObservableCollection DaysList and corresponding index for each event).
Below is the XAML, declaration, and method for DayIndex:
View:
<ComboBox Name="cboDay"
ItemsSource="{Binding DaysList, Mode=OneTime}"
DisplayMemberPath="fltDay"
SelectedIndex="{Binding DayIndex, Mode=TwoWay}"
IsEditable="True" />
ViewModel:
public ObservableCollection<Day> DaysList { get; set; }
private int _dayIndex;
public int DayIndex
{
get
{
// perform some calculation logic;
return _dayIndex;
}
set
{
if (_dayIndex != value)
{
_dayIndex = value;
RaisePropertyChanged("DayIndex");
}
}
}
How do I handle the declaration for dayIndex so it remains updated as the monthIndex does (so I can use its value with other code)?

Related

How to change datacontext at runtime with Mvvm

I have a graph that I want to change some ViewModel property so the whole graph would change accordingly.
The only property that I want to change here is "year", I tried to implement the INotifyPropertyChanged so the binding will cause the graph to change automatically, but it didn't work.
This is the model:
public class Model
{
public double rate { get; set; }
public string date { get; set; }
}
This is the ViewModel:
public class ViewModel :INotifyPropertyChanged
{
private string _year;
public string Year { get { return _year; } set { _year = value;UpdateData(); OnPropertyChanged("Year"); } }
public ViewModel()
{
_year = "2017";
UpdateData();
}
public void UpdateData()
{
int i,j;//Indexs that holds actuall api retrived values
string cR, urlContents;// cR- current rate in string format, urlContents - the whole Api retrived data
string c;//For api syntx, add 0 or not, depends on the current date syntax
this.CurrenciesHis = new ObservableCollection<Model>();//Model objects collection
HttpClient client = new HttpClient();
for (int l = 1; l < 13; l++)
{
if (l < 10)
c = "0";
else
c = "";
urlContents = client.GetStringAsync("http://data.fixer.io/api/"+(_year)+"-"+ c + l + "-01?access_key=&base=USD&symbols=EUR&format=1").Result;
i = urlContents.IndexOf("EUR");//Finds the desired value from api recived data
j = urlContents.IndexOf("}");
cR = urlContents.Substring(i + 5, (j - 2) - (i + 5));
CurrenciesHis.Add(new Model() { rate = Convert.ToDouble(cR), date = "01/" + l.ToString() });
}
}
public ObservableCollection<Model> CurrenciesHis { get; set; }
#region "INotifyPropertyChanged members"
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion
This is the View that based on third party control (I deleted alot of XAML and used bold letters to mark where is the actuall binding located):
<layout:SampleLayoutWindow x:Class="AreaChart.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ResizeMode="CanResizeWithGrip"
xmlns:chart="clr-namespace:Syncfusion.UI.Xaml.Charts;assembly=Syncfusion.SfChart.WPF"
xmlns:local="clr-namespace:PL"
xmlns:layout="clr-namespace:Syncfusion.Windows.SampleLayout;assembly=Syncfusion.Chart.Wpf.SampleLayout"
UserOptionsVisibility="Collapsed"
WindowStartupLocation="CenterScreen" Height="643.287" Width="1250.5"
Title="2017">
<Grid>
<Grid.Resources>
...........................................
<chart:AreaSeries x:Name="AreaSeries" EnableAnimation="True"
**XBindingPath="date"
Label="Favourite"
YBindingPath="rate"
ItemsSource="{Binding CurrenciesHis}"**
ShowTooltip="True" >
<chart:AreaSeries.AdornmentsInfo>
<chart:ChartAdornmentInfo AdornmentsPosition="Bottom"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ShowLabel="True">
<chart:ChartAdornmentInfo.LabelTemplate>
<DataTemplate>
....................................
<TextBox HorizontalAlignment="Left" Height="30" Margin="28,231,0,0" TextWrapping="Wrap" Name="Text1" VerticalAlignment="Top" Width="76" Text="{Binding Year, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
This is the code behaind and the event of the Textbox that I want to change with it's help that year property of the viewmodel:
public partial class MainWindow : SampleLayoutWindow
{
PL.ViewModel newInstance;
public MainWindow()
{
InitializeComponent();
newInstance = new PL.ViewModel();
this.DataContext = newInstance;
}
}
What I understand is that from this point the mechanism of WPFwill change the values on the chart using the binding and the "notification" of INotifyPropertyChanged but it doesn't work for me..
year should be a private field, but it is public. You're setting the value of the field, which naturally does not execute any of the code in the setter for the property.
Make year and all of your backing fields private, and rename all of your private fields with a leading underscore (for example, year should be renamed to _year) to prevent accidents like this.
And make it a policy in your viewmodel code always to set the property, never the field, except of course inside the actual property setter for that field.
Also, use bindings to set viewmodel properties from UI. Don't do it in codebehind. Get rid of that textchanged handler.
<TextBox
HorizontalAlignment="Left"
Height="30"
Margin="28,231,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="76"
Text="{Binding Year, UpdateSourceTrigger=PropertyChanged}"
/>
Finally, it seems that you intended for changes to Year to have some effect on the contents of CurrenciesHis, but there's no mechanism for that in your code, and no explanation of what you want to have happen or how you expect it to happen.
And here's an updated version of your viewmodel.
public class ViewModel
{
public ViewModel()
{
// DO NOT, DO NOT EVER, DO NOT, SERIOUSLY, EVER, EVER, EVER UPDATE A
// PROPERTY'S BACKING FIELD OUTSIDE THE PROPERTY'S SETTER.
Year = DateTime.Now.Year - 1;
UpdateCurrencies();
}
protected void UpdateCurrencies()
{
// Indexs that holds actuall api retrived values
int i, j;
// cR- current rate in string format, urlContents - the whole Api retrived data
string cR, urlContents;
// For api syntx, add 0 or not, depends on the current date syntax
string c;
CurrenciesHis = new ObservableCollection<Model>();//Model objects collection
HttpClient client = new HttpClient();
for (int l = 1; l < 13; l++)
{
if (l < 10)
c = "0";
else
c = "";
// Use the public property Year, not the field _year
var url = "http://data.fixer.io/api/" + Year + "-" + c + l + "-01?access_key=&base=USD&symbols=EUR&format=1";
urlContents = client.GetStringAsync(url).Result;
i = urlContents.IndexOf("EUR");//Finds the desired value from api recived data
j = urlContents.IndexOf("}");
cR = urlContents.Substring(i + 5, (j - 2) - (i + 5));
CurrenciesHis.Add(new Model() { rate = Convert.ToDouble(cR), date = "01/" + l.ToString() });
}
OnPropertyChanged(nameof(CurrenciesHis));
}
// Year is an integer, so make it an integer. The binding will work fine,
// and it'll prevent the user from typing "lol".
private int _year;
public int Year
{
get { return _year; }
set
{
if (_year != value)
{
_year = value;
OnPropertyChanged(nameof(Year));
UpdateCurrencies();
}
}
}
public ObservableCollection<Model> CurrenciesHis { get; private set; }
// -----------------------------------------------------
// YearsList property for ComboBox
// 30 years, starting 30 years ago.
// You could make this IEnumerable<int> or ReadOnlyCollection<int> if you
// want something other than the ComboBox to use it. The ComboBox doesn't care.
// Year MUST be an int for the binding to SelectedItem (see below) to work,
// not a string.
public System.Collections.IEnumerable YearsList
=> Enumerable.Range(DateTime.Now.Year - 30, 30).ToList().AsReadOnly();
}
XAML for YearsList combobox (which I prefer to the text box btw):
<ComboBox
ItemsSource="{Binding YearsList}"
SelectedItem="{Binding Year}"
/>
Your CurrenciesHis property doesn't implement INPC so WPF doesn't realize that you changed it (UpdateData() has "this.CurrenciesHis = new ObservableCollection();")
Your current property is:
public ObservableCollection<Model> CurrenciesHis { get; set; }
Should be something like this:
private ObservableCollection<Model> _CurrenciesHis;
public ObservableCollection<Model> CurrenciesHis { get { return _CurrenciesHis; } set { if (_CurrenciesHis != value) { _CurrenciesHis = value; OnPropertyChanged("CurrenciesHis"); } } }

Dynamic Row and Column Creation using WPF and MVVM

Note: I'm using MVVM Light Toolkit and MahApps.Metro.
I've checked the answers but it doesn't seem like any of them relate to my question.
I have a Grid whose columns and header should be dynamically created. The number and value of columns is unknown to view, and the number of rows is unknown to view.
Columns, rows and data in the rows represent a Database Table. All data is present in the ViewModel.
I have an ObservableCollection<ServerRow> ServerRows; in my ViewModel.
Server Row object is a Model that looks like this:
public class ServerRow : ObservableObject
{
private ObservableCollection<ServerColumn> _columns;
public ObservableCollection<ServerColumn> Columns
{
get { return _columns; }
set { Set(() => Columns, ref _columns, value); }
}
}
This is a ServerColumn class :
public class ServerColumn : ObservableObject
{
private string _name;
private string _type;
private string _value;
public string Name
{
get { return _name; }
set { Set(() => Name, ref _name, value); }
}
public string Type
{
get { return _type; }
set { Set(() => Type, ref _type, value); }
}
public string Value
{
get { return _value; }
set { Set(() => Value, ref _value, value); }
}
}
The Idea was to Bind DataGrid to ObservableCollection<ServerRow> ServerRows;, and then generate the Columns depending on the ServerRow object which has ServerColumns which in turn have Name (should be a header of the column), Type as the datatype of column data, and Value as the value which should be represented in every row/column.
My XAML is pretty simple (because it's not complete, and of course- not working)
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding ServerRows}"/>
How do I write the XAML properly to achieve what I'm trying to do?
This is the result, which makes sense because Grid is trying to show a collection of objects inside a single Column and calling its ToString() method.
I've had this problem before too.
If you look at what is done here:
https://github.com/taori/WMPR/blob/0a81bc6a6a4c6fc36edc4cbc99f0cfa8a2b8871c/src/WMPR/WMPR.Client/ViewModels/Sections/ReportEvaluationViewModel.cs#L503
You provide the iteratable collection as ObservableCollection<object> when the underlying structure is actually of type DynamicGridCell, which uses a DynamicGridCellDescriptor which can be found at
DynamicGridCell:
public class DynamicGridCell : DynamicObject, ICustomTypeDescriptor, IDictionary<string, object>
{
private readonly Dictionary<string, object> _values = new Dictionary<string, object>();
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return new AttributeCollection();
}
string ICustomTypeDescriptor.GetClassName()
{
return nameof(DynamicGridCell);
}
string ICustomTypeDescriptor.GetComponentName()
{
return null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return null;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return null;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return null;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return null;
}
private PropertyDescriptor[] CreatePropertyDescriptors()
{
var result = new List<PropertyDescriptor>();
foreach (var pair in _values)
{
result.Add(new DynamicGridCellDescriptor(pair.Key));
}
return result.ToArray();
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
var result = new PropertyDescriptorCollection(CreatePropertyDescriptors());
return result;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
var result = new PropertyDescriptorCollection(CreatePropertyDescriptors());
return result;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public IEnumerator GetEnumerator()
{
return _values.GetEnumerator();
}
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
return _values.GetEnumerator();
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
{
_values.Add(item.Key, item.Value);
}
void ICollection<KeyValuePair<string, object>>.Clear()
{
_values.Clear();
}
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
return _values.Contains(item);
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
if (_values.ContainsKey(item.Key))
{
_values.Remove(item.Key);
return true;
}
return false;
}
public int Count => _values.Count;
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
public bool ContainsKey(string key)
{
return _values.ContainsKey(key);
}
public void Add(string key, object value)
{
_values.Add(key, value);
}
bool IDictionary<string, object>.Remove(string key)
{
return _values.Remove(key);
}
public bool TryGetValue(string key, out object value)
{
return _values.TryGetValue(key, out value);
}
public object this[string key]
{
get { return _values[key]; }
set
{
if (_values.ContainsKey(key))
{
_values[key] = value;
}
else
{
_values.Add(key, value);
}
}
}
public ICollection<string> Keys => _values.Keys;
public ICollection<object> Values => _values.Values;
}
DynamicGridCellDescriptor
public class DynamicGridCellDescriptor : PropertyDescriptor
{
public DynamicGridCellDescriptor(string name) : base(name, null)
{
}
public override bool CanResetValue(object component)
{
return true;
}
public override object GetValue(object component)
{
return ((DynamicGridCell) component)[Name];
}
public override void ResetValue(object component)
{
((DynamicGridCell) component)[Name] = null;
}
public override void SetValue(object component, object value)
{
((DynamicGridCell) component)[Name] = value;
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override Type ComponentType => typeof(DynamicGridCell);
public override bool IsReadOnly => false;
public override Type PropertyType => typeof(object);
}
Just make sure that the property you bind to is of type ObservableCollection<object> anyways - otherwise for me automatic grid column generation did not work.
You have some logical issues.
When you set the ItemsSource of a DataGrid the bound collection will be used to create rows and if you don't change it the property AutoGenerateColumns is set to true. In this case the DataGrid will generate a column for each property in the bound collection and this is exactly what is happening in your sample.
You bound an instance with a property 'Columns' and get a DataGrid column which is named 'Columns'. And you get as much rows as you have entries in this property displayed as '(Collection)' because ServerColumn inherits from ObservableObject.
You can set AutoGenerateColumns to false and have to create the columns by your own; normally in xaml => hard coded.
If you really want to have dynamically generate the columns you have to write your own logic to create and bind the columns. I've done that once and it's pain in the ass if you want to have it generic.
If you want a DataGrid with dynamic columns where the user can change values it's more tricky then a read only one.
One approach could be having a ObservableCollection<string> for the column names and another one ObservableCollection which stores your ViewModels for each row.
If both rows and columns really need to be dynamic, your best choice is to use two nested ItemControls, the outer one representing rows, the inner one columns:
<ItemsControl ItemsSource="{Binding Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Columns}" ItemTemplateSelector="{StaticResource ColumnTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This allows you to display different type of columns, by defining a template selector that might look somewhat similar to the following:
public class ColumnTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var column = item as ServerColumn;
switch (column.Type)
{
case "Text":
return TextTemplate;
case "Number":
return NumberTemplate;
case "Image":
return ImageTemplate;
}
// Fallback
return TextTemplate;
}
public DataTemplate TextTemplate { get; set; }
public DataTemplate NumberTemplate { get; set; }
public DataTemplate ImageTemplate { get; set; }
}
...based on each column's type, a different template would be referenced (all of these template obviously need to be defined somewhere and referenced as StaticResource. (This even allows easy creation of changeable (not read-only) grids.)
Note that, instead of the outer ItemsControl, you can of course use ListView or any other control that is derived from ItemsControl. Using a ListView might be useful if you need automatic scrolling, for example.

WPF ComboBox SelectedItem when bound to an enum

I have a WPF ComboBox bound to a list of a class which contains an enum.
This all works fine, my question is at the end of this post, first the code:
Here is the class:
public class FILTER_TEST
{
public FilterType Filter { get; private set; }
public string Description { get; private set; }
public static List<FILTER_TEST> CreateFilters()
{
var list = new List<FILTER_TEST>();
list.Add(new FILTER_TEST() { Filter = FilterType.CheckNone, Description = "Uncheck all" });
list.Add(new FILTER_TEST() { Filter = FilterType.CheckAll, Description = "Check all" });
list.Add(new FILTER_TEST() { Filter = FilterType.CheckCustom, Description = "Custom check" });
return list;
}
}
Here is the enum FilterType:
public enum FilterType
{
CheckNone,
CheckAll,
CheckCustom
}
In my view model I have the following:
public List<FILTER_TEST> FilterNames { get { return FILTER_TEST.CreateFilters(); } }
public FILTER_TEST SelectedFilter
{
get { return selectedFilter; }
set
{
if (value != selectedFilter)
{
selectedFilter = value;
OnPropertyChanged("SelectedFilter");
}
}
}
Also in the view model, I set the SelectedItem of the ComboBox as follows:
SelectedFilter = FilterNames.Where(x => x.Filter == FilterType.CheckNone).FirstOrDefault();
Here is the xaml putting it all together:
<ComboBox DisplayMemberPath="Description" ItemsSource="{Binding FilterNames}"
SelectedItem="{Binding SelectedFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"/>
My problem is that although the changing of the SelectionItem works, the actual value displayed in the ComboBox doesn’t change.
The initial SelectedItem is “Uncheck all” as, when the window has been loaded, none of the corresponding CheckBox controls (bound to another class which contains a Boolean property) have been checked. What I would like is that when a CheckBox has been checked, then the SelectedItem changes to “Custom check”.
This does indeed change the value of the SelectedItem:
SelectedFilter = FilterNames.Where(x => x.Filter == FilterType.CheckCustom).FirstOrDefault();
But the text shown in the ComboBox is still “Uncheck all”.
Does anyone have an idea as to what I am missing? I am forced to use the 4.0 framework, I don’t know if this is relevant.
I've seen the hint to overwrite Equals() of the type in use as this:
public override bool Equals(object o)
{
if (o is FILTER_TEST)
{
var other = o as FILTER_TEST;
return this.Description == other.Description && this.Filter == other.Filter;
}
else
return false;
}
Now that makes your sample work. Let me come back for a reference on the why.

WPF/MVVM: SelectedIndex Binding Property not updating when Combobox Selection Changed

I've got a very similar question to Jinesh's. I need to get the value of the SelectedIndex (or SelectedItem or SelectedValue) of a combo box in to my ViewModel code (i.e., when a user selects an item in the combo box, I need that index (or item or value) to update a property in the ViewModel code). I've followed the suggestions in Sheridan's answer and Felix C's answer but my property is not getting updated when the selected item changes. I'm trying to get the newly selected index value as part of my "Update" button code.
View:
<ComboBox Name="cboMonth"
ItemsSource="{Binding MonthsList, Mode=OneTime}"
DisplayMemberPath="month"
SelectedIndex="{Binding MonthIndex, Mode=TwoWay}"/>
<Button Name="btnUpdate"
Content="Update"
Command="{Binding processUpdateButton}" />
ViewModel:
public ObservableCollection<Month> MonthsList { get; set; }
private int _monthIndex;
public int MonthIndex
{
get
{
DateTime today = DateTime.Today;
_monthIndex = today.Month - 1;
return _monthIndex;
}
set
{
if (_monthIndex != value)
{
_monthIndex = value;
RaisePropertyChanged("MonthIndex");
}
}
}
public ICommand processUpdateButton
{
get
{
if (_setUpdateButton == null)
{
_setUpdateButton = new RelayCommand(param => validateOdometer());
}
return _setUpdateButton;
}
}
public void validateOdometer()
{
Console.WriteLine("Validating odometer...");
Console.WriteLine("Month Index: " + (_monthIndex));
}
When my page first renders, I have the combo box defaulting to index 5 (06-June) and the property in question, _monthIndex, reflects 5. When I select a new month in the combo box (e.g., October) then click my update button (btnUpdate), _monthIndex should reflect 9 but it still reflects 5. Why? Appreciate any/all help. Thanks.
The property getter ignores the previously set value and always returns the index of the current month.
The declaration should look like this:
private int monthIndex = DateTime.Today.Month - 1;
public int MonthIndex
{
get { return monthIndex; }
set
{
if (monthIndex != value)
{
monthIndex = value;
RaisePropertyChanged("MonthIndex");
}
}
}

Combobox in settingsflyout does not show selected values when opening it again

This seemed so simple but has turned into a nightmare for me. Everything works great, i can select a value and it's reported back to the view model.
Problem:
User opens the settings flyout and selects a value. User exits the flyout.
User reopens the settings flyout and there is no selected value in the combobox. The value exists in the view model though.
Scenario:
Combobox in a Settingsflyout.
<ComboBox x:Name="defaultComboBox" SelectedItem="{Binding UserSettings.DefaultAccount, Mode=TwoWay}" ItemsSource="{Binding UserAccounts}" DisplayMemberPath="CustomName">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:InvokeCommandAction Command="{Binding UserAccountComboboxLoadedCommand}" CommandParameter="{Binding ElementName=defaultAccountComboBox}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ComboBox>
ViewModelCode:
public void Open(object parameter, Action successAction)
{
logger.LogProgress("Opened UserSettingsFlyoutView.");
UserSettings.DefaultAccount = UserAccounts.FirstOrDefault(u => u.AccountID.ToString().Equals(userSettings.DefaultAccountGuid,StringComparison.CurrentCultureIgnoreCase));
}
public CrossThreadObservableCollection<UserAccount> UserAccounts
{
get
{
try
{
return dbContext.RetrieveAllUserAccounts();
}
catch(Exception e)
{
logger.LogError("Error happened when retrieving user-accounts from secure data store Error: " + e.Message, e.ToString());
return new CrossThreadObservableCollection<UserAccount>();
}
}
}
private IProvideUserSetting userSettings;
public IProvideUserSetting UserSettings
{
get { return userSettings; }
set { userSettings = value; OnPropertyChanged("UserSettings"); }
}
UserSettings class:
private string defaultAccountGuid;
[DataMember]
public string DefaultAccountGuid
{
get { return defaultAccountGuid; }
set { defaultAccountGuid = value; OnPropertyChanged("DefaultAccountGuid"); }
}
private UserAccount defaultAccount;
[IgnoreDataMember]
public UserAccount DefaultAccount
{
get { return defaultAccount; }
set {
defaultAccount = value;
if (defaultAccount != null)
DefaultAccountGuid = defaultAccount.AccountID.ToString();
OnPropertyChanged("DefaultAccount"); }
}
I tried a version of the code and could not reproduce the issue. Could you provide more code? Is there something else setting the selected item?
Anyway, the type of item in the ItemsSource is different than the type of item for selected item. I would try changing the selected item binding to the same class in the items source.
For example, instead of the viewmodel property UserSettings, make that object type UserAccount.
Something like
private UserAccount _selectedUserAccount { get; set; }
public UserAccount SelectedUserAccount
{
get { return _selectedUserAccount; }
set
{
if (_selectedUserAccount != value)
{
_selectedUserAccount = value;
OnPropertyChanged("SelectedUserAccount");
}
}
}
Edit:
You can add a loaded event handler to your combobox, then locate the viewmodel from the code behind and set the selected item property.
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
ComboBox comboBox = sender as ComboBox;
comboBox.SelectedItem =
_viewModel.UserAccounts.Where(x => x.UserAccountString == _viewModel.SelectedUserAccount.UserAccountString);
}

Resources