Binding not working on a single property of an object (while other properties are working) - c#

I'm making a configurable WPF input dialog with the following code:
InputMessageBox.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:MediaManager.Forms" x:Class="MediaManager.Forms.InputMessageBox"
Title="{Binding Title}" Height="{Binding Height}" Width="{Binding Width}">
<Window.Background>
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ControlColorKey}}" />
</Window.Background>
<Grid>
<xctk:WatermarkTextBox Watermark="{Binding Message}" Margin="10" VerticalAlignment="Top" TabIndex="0" />
<Button Content="{Binding CancelButtonText}" Width="{Binding ButtonWidth}" Margin="10" HorizontalAlignment="Right" VerticalAlignment="Bottom" IsCancel="True" TabIndex="2" />
<Button Content="{Binding OkButtonText}" Width="{Binding ButtonWidth}" Margin="{Binding MarginOkButton}" HorizontalAlignment="Right" VerticalAlignment="Bottom" IsDefault="True" TabIndex="1" />
</Grid>
InputMessageBox.xaml.cs
public partial class InputMessageBox : Window
{
public InputMessageBox(inputType inputType)
{
InitializeComponent();
switch (inputType)
{
case inputType.AdicionarConteudo:
{
Properties = new InputMessageBoxProperties()
{
ButtonWidth = 75,
CancelButtonText = "Cancelar",
Height = 108,
Message = "Digite o nome do conteudo a ser pesquisado...",
OkButtonText = "Pesquisar",
Title = string.Format("Pesquisar - {0}", Settings.Default.AppName),
Width = 430
};
break;
}
default:
break;
}
DataContext = Properties;
}
public InputMessageBoxProperties Properties { get; set; }
}
InputMessageBoxProperties.cs
public class InputMessageBoxProperties
{
public int ButtonWidth { get; set; }
public string CancelButtonText { get; set; }
public int Height { get; set; }
public string InputText { get; set; }
public Thickness MarginOkButton { get { return new Thickness(10, 10, ButtonWidth + 15, 10); } }
public string Message { get; set; }
public string OkButtonText { get; set; }
public string Title { get; set; }
public int Width { get; set; }
}
When i call it, every binding is working as expected but one, the Width property. When i debug, the width property value is 430, but the width of the frame itself is a lot bigger than that. The confusing part is that the rest of the binding are working. Why is it happening?

You can fix that by setting the Width's Binding Mode to TwoWay :
Width="{Binding Width,Mode=TwoWay}"
You might as well consider implementing the INotifyPropertyChanged interface, so that the UI will automatically be notified in case any changes occurs in those properties :
public class InputMessageBoxProperties:INotifyPropertyChanged
{
private int _width ;
public int Width
{
get
{
return _width;
}
set
{
if (_width == value)
{
return;
}
_width = value;
OnPropertyChanged();
}
}
// add the other properties following the same pattern
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}

Related

How refresh view after added new item

I have problem because if I add new item in my observablecollection this i don't see result in my view.
I need a restart, then I can see the new item.
Here is my viewmodel where i dispaly items
public class ManageFleetListingViewModel : ViewModelBase
{
private readonly Func<IEnumerable<DisplayManageFleetViewModel>,IEnumerable<DisplayManageFleetViewModel>> _filtersVehicle;
private readonly ObservableCollection<DisplayManageFleetViewModel> _manageFleetViewModel;
private readonly VehicleState _vehicleState;
private readonly IManageFleetService _manageFleetService;
public IEnumerable<DisplayManageFleetViewModel> Vehicles => _manageFleetViewModel;
public ICommand DeleteVehicleCommand { get; set; }
public ManageFleetListingViewModel(VehicleState vehicleState, IManageFleetService manageFleetService) : this(vehicleState,manageFleetService, manageFleet => manageFleet) { }
public ManageFleetListingViewModel(VehicleState vehicleState, IManageFleetService manageFleetService, Func<IEnumerable<DisplayManageFleetViewModel>, IEnumerable<DisplayManageFleetViewModel>> filtersVehicle )
{
DeleteVehicleCommand = new DeleteVehicleCommand(this, manageFleetService);
_filtersVehicle = filtersVehicle;
_vehicleState = vehicleState;
_manageFleetViewModel = new ObservableCollection<DisplayManageFleetViewModel>();
_vehicleState.StateChanged += VehicleState_StateChanged;
DisplayVehicles();
}
public void DeleteItem(int id)
{
var item = Vehicles.FirstOrDefault(x => x.Id == id);
_manageFleetViewModel.Remove(item);
}
public void AddItem()
{
DisplayVehicles();
}
private void DisplayVehicles()
{
IEnumerable<DisplayManageFleetViewModel> displayManageFleets = _vehicleState.GetVehicles
.Select(s => new DisplayManageFleetViewModel(s.Id, s.CarBrand, s.VIN, s.Milage, s.EnigneNumber, s.EngineCapacity, s.RegistrationNumber, s.FirstRegistration, s.YearPurchase, s.YearProduction, s.ImageCar));
displayManageFleets = _filtersVehicle(displayManageFleets);
_manageFleetViewModel.Clear();
foreach (DisplayManageFleetViewModel viewModel in displayManageFleets)
{
_manageFleetViewModel.Add(viewModel);
}
}
private void VehicleState_StateChanged()
{
DisplayVehicles();
}
This is my project domain where I add item
public class ManageFleetService : IManageFleetService
{
private readonly IDataService<Account> _accountService;
private readonly IVehicleService _vehicleService;
public ManageFleetService(IDataService<Account> accountService, IVehicleService vehicleService)
{
_accountService = accountService;
_vehicleService = vehicleService;
}
public async Task<Account> AddVehicle(string carBrand, string vin, string milage, string engineNumber, string engineCapacity, string registerNumber, DateTime firstRegistration, DateTime yearPurchase, DateTime yearProduction, byte[] imageCar,Account accountId)
{
Vehicle vehicleVIN = await _vehicleService.GetByVIN(vin);
if(vehicleVIN != null)
{
throw new InvalidVinNumberException(vin);
}
Vehicle vehicleRegistraion = await _vehicleService.GetByRegistrationNumber(registerNumber);
if(vehicleRegistraion != null)
{
throw new InvalidRegistrationNumberException(registerNumber);
}
Vehicle vehicle = new Vehicle()
{
CarBrand = carBrand,
VIN = vin,
Milage = milage,
EnigneNumber = engineNumber,
EngineCapacity = engineCapacity,
RegistrationNumber = registerNumber,
FirstRegistration = firstRegistration,
YearPurchase = yearPurchase,
YearProduction = yearProduction,
ImageCar = imageCar,
Account = accountId
};
accountId.Vehciles = new List<Vehicle>();
accountId.Vehciles.Add(vehicle);
await _accountService.Update(accountId, accountId.Id);
return accountId;
}
When the item is added to the database i display next time functions DisplayVehicles in Command
Account account = await _manageFleetService.AddVehicle(carBrand, vin, milage, engineCapacity, engineCapacity, registerNumber, firstRegistration, yearPurchase, yearProduction, imageCar, _accountStore.CurrentAccount);
_manageFleetListingViewModel.AddItem();
This is my userconrol (ManageFleetListing) xaml where i display items
<Grid>
<StackPanel HorizontalAlignment="Center">
<ItemsControl ItemsSource="{Binding Vehicles}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Task:VehcileTask/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
Here is my pattern for VehicleTask
<StackPanel>
<Border>
</Border>
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
</Grid>
<StackPanel Grid.Column="0" MinWidth="150" MaxWidth="300">
<TextBlock Text="{Binding CarBrand}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<StackPanel Grid.Column="1" MinWidth="150" MaxWidth="300">
<TextBlock Text="{Binding VIN}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<StackPanel Grid.Column="2" MinWidth="100" MaxWidth="200">
<TextBlock Text="{Binding Milage}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<StackPanel Grid.Column="3" MinWidth="100" MaxWidth="200" Margin="0 0 20 0">
<TextBlock Text="{Binding YearProduction, StringFormat='dd/MM/yyyy'}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<Button Content="Edit" Command="{Binding Path=DataContext.DeleteVehicleommand, RelativeSource={RelativeSource AncestorType=local:ManageFleetListing}}"/>
<Button Background="Red" BorderThickness="0" Content="Delete" Command="{Binding Path=DataContext.DeleteVehicleCommand, RelativeSource={RelativeSource AncestorType=local:ManageFleetListing}}" Margin="10 0 0 0" CommandParameter="{Binding Id}"/>
</StackPanel>
<Border BorderThickness="1" Background="Black"></Border>
</StackPanel>
This is main view here i use CreateVehicleCommandand and display ManageFleetListing
<Button
Command="{Binding CreateVehicleCommand}"
Style="{DynamicResource InventoryButton}" Height="50" Width="200" HorizontalAlignment="Left" Margin="40 20 0 0">
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF5DFF00"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
<Button.Content>
<TextBlock Text="Add" FontSize="20" FontWeight="Bold" Foreground="Gray"/>
</Button.Content>
</Button>
<Grid Grid.Row="5" Height="200" Margin="0 10 0 0">
<ManageFleet:ManageFleetListing DataContext="{Binding ManageFleetListingViewModel}"/>
</Grid>
and this is view model for xaml above
public class ManageFleetViewModel : ViewModelBase
{
public ICommand CreateVehicleCommand { get; set; }
public ManageFleetListingViewModel ManageFleetListingViewModel { get; }
public ManageFleetViewModel(IManageFleetService menageFleetService, IAccountStore accountStore,VehicleState vehicleState, IManageFleetService manageFleetService,ManageFleetListingViewModel manageFleetListingViewModel)
{
CreateVehicleCommand = new CreateVehicleCommand(this, menageFleetService, accountStore, manageFleetListingViewModel);
ManageFleetListingViewModel = new ManageFleetListingViewModel(vehicleState,manageFleetService);
}
private string _carbrand { get; set; } //if is problem with added to database look here
private string _vin { get; set; }
private string _milage { get; set; }
private string _enigneNumber { get; set; }
private string _engineCapacity { get; set; }
private string _registrationNumber { get; set; }
private DateTime _firstRegistration { get; set; }
private DateTime _yearPurchase { get; set; }
private DateTime _yearProduction { get; set; }
private byte [] _imageCar { get; set; }
public string CarBrand
{
get
{
return _carbrand;
}
set
{
_carbrand = value;
OnPropertyChanged(nameof(CarBrand));
}
}
public string VIN
{
get
{
return _vin;
}
set
{
_vin = value;
OnPropertyChanged(nameof(VIN));
}
}
public string Milage
{
get
{
return _milage;
}
set
{
_milage = value;
OnPropertyChanged(nameof(Milage));
}
}
public string EnigneNumber
{
get
{
return _enigneNumber;
}
set
{
_enigneNumber = value;
OnPropertyChanged(nameof(EnigneNumber));
}
}
public string EngineCapacity
{
get
{
return _engineCapacity;
}
set
{
_engineCapacity = value;
OnPropertyChanged(nameof(EngineCapacity));
}
}
public string RegistrationNumber
{
get
{
return _registrationNumber;
}
set
{
_registrationNumber = value;
OnPropertyChanged(nameof(RegistrationNumber));
}
}
public DateTime FirstRegistration
{
get
{
if(_firstRegistration.Year == 1) { return DateTime.Now; }
return _firstRegistration;
}
set
{
_firstRegistration = value;
OnPropertyChanged(nameof(FirstRegistration));
}
}
public DateTime YearPurchase
{
get
{
if(_yearPurchase.Year == 1) { return DateTime.Now; }
return _yearPurchase;
}
set
{
_yearPurchase = value;
OnPropertyChanged(nameof(YearPurchase));
}
}
public DateTime YearProduction
{
get
{
if (_yearProduction.Year == 1) { return DateTime.Now; }
return _yearProduction;
}
set
{
_yearProduction = value;
OnPropertyChanged(nameof(YearProduction));
}
}
public byte [] ImageCar
{
get
{
return _imageCar;
}
set
{
_imageCar = value;
OnPropertyChanged(nameof(ImageCar));
}
}
Below is the accepted answer but as you can see from the comments my statements are not true.
You have to bind to an ObservableCollection<T> for the control to subscribe to changes to the collection. In your case you bind to IEnumerable<T> so the control is populated from this list once. Changes to the ObservableCollection<T> backing the IEnumerable<T> are never seen by the control.
public IEnumerable<DisplayManageFleetViewModel> Vehicles => _manageFleetViewModel;
Simply change this to
public ObservableCollection<DisplayManageFleetViewModel> Vehicles => _manageFleetViewModel;
Or perhaps even better get rid of the _manageFleetViewModel field and change Vehicles:
public ObservableCollection<DisplayManageFleetViewModel> Vehicles { get; }
Then use Vehicles instead of _manageFleetViewModel in your code.

Bind UserControl Property to ViewModel

I have an UserContol Combination of Country Code Combobox and A number Textbox.
In my Window, I have 2 UserControl (Primary Phone Number and Secondary Phone Number)
Here is my Model
public class ContactNumber
{
public Country Country { get; set; }
public string Number { get; set; }
}
public class Country
{
public int id { get; set; }
public string name { get; set; }
public string code { get; set; }
public string phone_code { get; set; }
public int is_active { get; set; }
public List<Country> country { get; set; }
}
Here is My UserControl XAML
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="250"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--ComboBox for Country-->
<ComboBox Grid.Column="0" Style="{StaticResource ComboBoxMerged}" x:Name="cmbCountry"></ComboBox>
<TextBlock Text="|" FontSize="{StaticResource fontSize30}" Foreground="{StaticResource LightSilverBrush}"
HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Column="1" Margin="0,-5,0,0">
</TextBlock>
<!--TextBox for Number-->
<TextBox Style="{StaticResource TextBoxWithDropDown}"
Text="{Binding ContactNumber.Number,ElementName=ContactWindow,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
x:Name="txtContactNumber" TextChanged="txtContactNumber_TextChanged" Width="240" Grid.Column="2">
</TextBox>
</Grid>
Here is my Control Code Behind
public ContactNumber ContactNumber
{
get { return (ContactNumber)GetValue(ContactNumberProperty); }
set
{
SetValue(ContactNumberProperty, value);
}
}
// Using a DependencyProperty as the backing store for ContactNumber. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContactNumberProperty =
DependencyProperty.Register("ContactNumber", typeof(ContactNumber), typeof(ContactNumberWithCountryCode),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
SetText));
private static void SetText(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ContactNumberWithCountryCode contactNumberWithCountryCode=d as ContactNumberWithCountryCode;
if (contactNumberWithCountryCode!=null)
{
contactNumberWithCountryCode.txtContactNumber.Text = (e.NewValue as ContactNumber).Number.ToString();
}
}
public ContactNumberWithCountryCode()
{
InitializeComponent();
}
private void txtContactNumber_TextChanged(object sender, TextChangedEventArgs e)
{
ContactNumber.Number = txtContactNumber.Text;
}
This is my Main Window XAMl
<usercontrols:ContactNumberWithCountryCode ContactNumber="{Binding PrimaryContactNumber,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" x:Name="PracticePhoneNumber" Margin="0,15,0,0" ></usercontrols:ContactNumberWithCountryCode>
<usercontrols:ContactNumberWithCountryCode ContactNumber="{Binding SecondryContactNumber,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" x:Name="SecondaryPracticePhoneNumber" Margin="0,15,0,0" ></usercontrols:ContactNumberWithCountryCode>
Here is my View Model Code
private ContactNumber primaryContactNumber;
public ContactNumber PrimaryContactNumber
{
get { return primaryContactNumber; }
set
{
primaryContactNumber = value;
OnPropertyChanged("PrimaryContactNumber");
}
}
private ContactNumber secondryContactNumber;
public ContactNumber SecondryContactNumber
{
get { return secondryContactNumber; }
set
{
secondryContactNumber = value;
OnPropertyChanged("SecondryContactNumber");
}
}
My goal is to fill ViewModel Property on Text Or Combobox Value Change.
Thanks in Advance.

How to add textbox values to list using MVVM?

Models
public class EmployeeDetails
{
public string Name { get; set; }
public int Age {get;set;}
}
public class AddressDetails
{
public EmployeeDetails EmployeeName { get; set; }
public string City { get; set; }
}
View
<Window x:Class="ClassCollection.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:ClassCollection"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding Source={StaticResource loc},Path=ViewModel}"
>
<Grid>
<StackPanel Margin="0 20 0 0">
<TextBox x:Name="txt1" Width="90" Height="20" Text="{Binding Details.EmployeeName}"/>
<TextBox x:Name="txt2" Width="90" Height="20" Text="{Binding Details.City}" Margin="0 20 0 0"/>
</StackPanel>
<Button x:Name="btn" Width="90" Height="25" Content="Add" Command=" {Binding AddCommand}"/>
</Grid>
</Window>
ViewModel
public class Viewmodel
{
public ObservableCollection<AddressDetails> EmployeeList;
public Viewmodel()
{
EmployeeList = new ObservableCollection<AddressDetails>();
LoadCommand();
}
private AddressDetails _details;
public AddressDetails Details
{
get { return _details; }
set
{
_details = value;
}
}
// Commands
public ICommand AddCommand { get; set; }
private void LoadCommand()
{
AddCommand = new CustomCommand(Add, CanAdd);
}
private bool CanAdd(object obj)
{
return true;
}
private void Add(object obj)
{
EmployeeList.Add(new AddressDetails { EmployeeName = Details.EmployeeName, City = Details.City });
}
}
Locator
public class Locator
{
private static Viewmodel viewmodel = new Viewmodel();
public static Viewmodel ViewModel
{
get { return viewmodel; }
}
}
How to add TextBox value to collection list using MVVM?
The Above is my code that I have tried. It shows null reference exception if I do like above. What would be the problem?
Update
I have two fields in EmployeeDetails class. So I must give input for these two field when add to collection. But I need only one field Name to insert to the collection. How to do it?
Analysis
It seems the _details field is not «initialized».
Solution
Please consider introducing the appropriate field initialization, for example:
private readonly AddressDetails _details = new AddressDetails
{
EmployeeName = new EmployeeDetails()
};

Cannot access a checkbox in a datatemplate [duplicate]

This question already has an answer here:
How to access a child control's property when it is declared in a ControlTemplate?
(1 answer)
Closed 2 years ago.
<GroupBox x:Name="CrashGenerationGroupBox" Header="Crash Generation" Margin="5" FontSize="18" FontWeight="SemiBold">
<GroupBox.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="cbHeaderCrashGeneration"/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</GroupBox.HeaderTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton GroupName="CrashGeneration" Content="Oscar" IsEnabled="{Binding ElementName=cbHeaderCrashGeneration, Path=IsChecked}"/>
<RadioButton GroupName="CrashGeneration" Content="CrashSimulator" IsEnabled="{Binding ElementName=cbHeaderCrashGeneration, Path=IsChecked}"/>
</StackPanel>
</GroupBox>
I am trying to access the IsChecked property of the CheckBox defined in the header template of the GroupBox. But i see i can't access that CheckBox state. I've tried also to use in the code behind and it's not available also. Can somebody give me a hint here?
Your XAML will look like this...
<Grid>
<DataGrid x:Name="datagrid1" AutoGenerateColumns="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Select Value">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="Chk" Tag="{Binding}" Checked="Chk_Checked"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Name="ChkAll" Checked="ChkAll_Checked" Unchecked="ChkAll_Unchecked" IsThreeState="False" Padding="4,3,4,3" HorizontalContentAlignment="Center" HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
And the code behind would be like this:
public partial class MainWindow : Window
{
private ObservableCollection<customer> custcol;
public ObservableCollection<customer> custCol
{
get { return custcol; }
set
{
custcol = value;
}
}
public MainWindow()
{
InitializeComponent();
custcol = new ObservableCollection<customer>();
custCol.Add(new customer { custID = 1, custName = "1", Status = "InActive", Flag = true });
custCol.Add(new customer { custID = 2, custName = "2", Status = "InActive", Flag = false });
custCol.Add(new customer { custID = 3, custName = "3", Status = "InActive", Flag = false });
datagrid1.ItemsSource = this.custCol;
}
private void ChkAll_Checked(object sender, RoutedEventArgs e)
{
}
private void ChkAll_Unchecked(object sender, RoutedEventArgs e)
{
}
private void Chk_Checked(object sender, RoutedEventArgs e)
{
switch (((sender as CheckBox).Tag as customer).custID)
{
case 1: break;
case 2: break;
case 3: break;
}
}
}
public class customer : INotifyPropertyChanged
{
public object obj { get; set; }
public int custID { get; set; }
private string custname;
public string custName
{
get { return custname; }
set
{
custname = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("custName"));
}
}
}
public DateTime startTime { get; set; }
public DateTime endTime { get; set; }
private string status;
public string Status
{
get { return status; }
set
{
status = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Status"));
}
}
}
private string duration;
public string Duration
{
get { return duration; }
set
{
duration = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Duration"));
}
}
}
public bool Flag { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}

Setting a property in VM from current row in observable collection

All -
I am trying to set public property in VM based on the current item in Observable collection (which is also in VM). So essentially - I want to set shadecolor as Blue or Pink based on the row I am (see sample code below). Also see image of what the end result will look like.
Can somebody please suggest - how I can achieve this - am really stuck with this problem
See sample code below:
Model.cs
public class Model
{
public Employee empdetails { get; set; }
}
public class Employee
{
public string fname { get; set; }
public string lname { get; set; }
public Enum gender { get; set; }
}
public enum gender
{
Male,
Female
}
ViewModel.cs
public class ViewModel
{
public ObservableCollection<Model> employees {get; set;}
public myCommand NextCommand { get; set; }
private Color _shadecolor;
public Color shadecolor
{
get
{
return _shadecolor;
}
set
{
_shadecolor = value;
}
}
public ViewModel()
{
employees = new ObservableCollection<Model>()
{
#region Populating Emp 1
new Model()
{
empdetails = new Employee()
{
fname = "John",
lname = "Smith",
gender = gender.Male
}
},
#endregion
#region Populating Emp 2
new Model()
{
empdetails = new Employee()
{
fname = "Robert",
lname = "Ally",
gender = gender.Female
}
},
#endregion
};
NextCommand = new myCommand(myNextCommandExecute, myCanNextCommandExecute);
}
private void myNextCommandExecute(object parameter)
{
}
private bool myCanNextCommandExecute(object parameter)
{
return true;
}
}
View.xaml
<Window x:Class="WpfApplication1.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="View" Height="500" Width="500" WindowStyle="None" AllowsTransparency="True" Background="Transparent">
<Border VerticalAlignment="Top" HorizontalAlignment="Left" BorderBrush="Silver" BorderThickness="2" CornerRadius="15">
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.511,0.957">
<GradientStop Color="LightGray" Offset="0.55" />
<GradientStop Color="{Binding shadecolor}" Offset="1.3" />
</LinearGradientBrush>
</Border.Background>
<Grid Width="300" Height="300" Margin="3">
<StackPanel VerticalAlignment="Top" >
<TextBlock Text="{Binding Path=employees/empdetails.fname}" />
<Button Command="{Binding NextCommand}" Content="Next" Width="100"></Button>
</StackPanel>
</Grid>
</Border>
</Window>
I believe what you want is to bind SelectedItem={Binding SelectedItem} where Selected item is on the view model as well, exposed as an observable property.
public Model SelectedItem
{
...
}
I'm not totally sure what you are trying to achieve here though as you don't have anything in your XAML deriving from Selector, therefore there is no concept of a selected item here.
Why not use a ValueConverter?
<GradientStop Color="{Binding Path=gender, Converter={StaticResource GenderToColorConverter}" Offset="1.3" />
Then inside your value converter:
If value == Gender.Male return blue;
return pink;
Technically, I think you return a Brush, but don't quote me on that.
Here's some sample code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel(GetTestEmployees());
}
static IEnumerable<Employee> GetTestEmployees()
{
return new[]
{
new Employee()
{
FirstName = "Tom",
LastName = "Selleck",
Gender = Gender.Male
},
new Employee()
{
FirstName = "Pat",
LastName = "Sajak",
Gender = Gender.Male,
},
new Employee()
{
FirstName = "Mae",
LastName = "West",
Gender = Gender.Female
}
};
}
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel(IEnumerable<Employee> employees)
{
_employees = new ObservableCollection<Employee>(employees);
SelectedEmployee = employees.First();
}
ObservableCollection<Employee> _employees;
public ObservableCollection<Employee> Employees
{
get { return _employees; }
}
Employee _selectedEmployee;
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
RaisePropertyChangedEvent("SelectedEmployee");
}
}
public void Next()
{
var curr = Employees.IndexOf(_selectedEmployee);
if (curr == -1) throw new ArgumentOutOfRangeException();
var next = (curr + 1) % Employees.Count;
SelectedEmployee = Employees[next];
}
ICommand _nextCommand;
public ICommand NextCommand
{
get
{
if (_nextCommand == null)
_nextCommand = new NextCommand(this);
return _nextCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class NextCommand : ICommand
{
ViewModel _viewModel;
public NextCommand(ViewModel viewModel)
{
_viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
//throw new NotImplementedException();
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
//throw new NotImplementedException();
_viewModel.Next();
}
}
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Gender Gender { get; set; }
}
public enum Gender
{
Male,
Female
}
public class GenderToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var gender = (Gender)value;
if (gender == Gender.Male)
{
return Colors.Blue;
}
return Colors.Pink;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And here's the corresponding markup:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<loc:GenderToColorConverter x:Key="GenderToColorConverter"/>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee}">
<ListBox.Template>
<ControlTemplate TargetType="ListBox">
<Grid>
<ContentControl DataContext="{TemplateBinding SelectedItem}">
<StackPanel >
<StackPanel.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.511,0.957">
<GradientStop Color="LightGray" Offset="0.55" />
<GradientStop Color="{Binding Path=Gender, Converter={StaticResource GenderToColorConverter}}" Offset="1.3" />
</LinearGradientBrush>
</StackPanel.Background>
<TextBox Text="{Binding FirstName}"/>
<TextBox Text="{Binding LastName}"/>
</StackPanel>
</ContentControl>
</Grid>
</ControlTemplate>
</ListBox.Template>
</ListBox>
<Button VerticalAlignment="Bottom" HorizontalAlignment="Center" Content="Next" Command="{Binding NextCommand}"/>
</Grid>
</Window>
I was able to implement this (fully working) solution using the approach Firoso has mentioned (leveraging the best practice too by Josh of keeping UI logic out of VM).
Posting the full code snippet/image for benefit of others.
Model
public class Model : CommonBase
{
public Employee empdetails { get; set; }
}
public class Employee : CommonBase
{
private string _fname;
public string fname
{
get
{
return _fname;
}
set
{
_fname = value;
OnPropertyChanged("fname");
}
}
public string lname { get; set; }
private Enum _gender;
public Enum gender
{
get
{
return _gender;
}
set
{
_gender = value;
OnPropertyChanged("gender");
}
}
}
public enum gender
{
Male,
Female
}
ViewModel
public class ViewModel
{
public Model employees { get; set; }
public myCommand NextCommand { get; set; }
public ViewModel()
{
employees = new Model()
{
empdetails = new Employee()
{
fname = "John",
lname = "Doe",
gender = gender.Male
}
};
NextCommand = new myCommand(myNextCommandExecute, myCanNextCommandExecute);
}
private void myNextCommandExecute(object parameter)
{
employees.empdetails.fname = "Ally";
employees.empdetails.lname = "Smith";
employees.empdetails.gender = gender.Female;
}
private bool myCanNextCommandExecute(object parameter)
{
return true;
}
}
View
<Window x:Class="WpfApplication1.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:WpfApplication1"
Title="View" Height="500" Width="500" WindowStyle="None" AllowsTransparency="True" Background="Transparent">
<Window.Resources>
<loc:GendertoColorConverter x:Key="GendertoColorConverter"/>
</Window.Resources>
<Border VerticalAlignment="Top" HorizontalAlignment="Left" BorderBrush="Silver" BorderThickness="2" CornerRadius="15">
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.511,0.957">
<GradientStop Color="LightGray" Offset="0.55" />
<GradientStop Color="{Binding Path=employees.empdetails.gender, Converter={StaticResource GendertoColorConverter}}" Offset="1.3" />
</LinearGradientBrush>
</Border.Background>
<Grid Width="300" Height="300" Margin="3">
<StackPanel VerticalAlignment="Top" >
<TextBlock Text="{Binding Path=employees.empdetails.fname}" />
<Button Command="{Binding NextCommand}" Content="Next" Width="100"></Button>
</StackPanel>
</Grid>
</Border>

Categories

Resources