View.xaml
<DataGrid ItemsSource="{Binding RentItems}" SelectedItem="{Binding ID.EquipmentID, Mode=TwoWay}" x:Name="datagrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding EquipmentID, Mode=OneWay}" IsReadOnly="True"/>
</DataGrid.Columns>
Viewmodel
public class ViewModelReturnData : ViewModelBase
{
public ICommand MyCommand { get; set; }
private ObservableCollection<Equipment> rentitems = new ObservableCollection<Equipment>();
public ObservableCollection<Equipment> RentItems
{
get { return rentitems; }
set { rentitems = value; }
}
private Equipment id;
public Equipment ID
{
get { return id; }
set { id = value; ; OnPropertyChanged("ID"); }
}
public ViewModelReturnData()
{
MyCommand = new RelayCommand(ExecuteMyMethod, CanExecuteMyMethod);
}
private bool CanExecuteMyMethod(object parameter)
{
return true;
}
private void ExecuteMyMethod(object parameter)
{
using (rentalEntities context = new rentalEntities())
{
var query = ...
foreach (var item in query)
{
RentItems.Add(item);
}
}
}
}
In ViewModelBase I got the INotifyPropertyChanged.
I Would like to bind value of EquipmentID to my property ID, but I don't know why it is not working. Seems like I missed something in binding. Binding the value like this Text="{Binding ElementName=datagrid1, Path=SelectedItem.EquipmentID}" works, but I have to bind in to property.
Related
I created a User Control page (SubPODetailView.xaml) that contains some entities "that entities defined from SubPO.cs" and a datagridview "that gridview is a separated class called SubInvoice.cs", and everything went good but in the datagridview, one of entities is a combobox display, the problem here is the collection list of this combobox is not displayed when running a program.
SubPO.cs
public class SubInvoice
{
public int Id { get; set; }
[Required]
public string InvoiceName { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
public List<string> Status{ get; set; }
public int SubPOId { get; set; }
public SubPO SubPO { get; set; }
}
Configuration.cs (In Migration Folder)
context.SubInvoices.AddOrUpdate(i => i.InvoiceName,
new SubInvoice
{
InvoiceName = "Invoice1",
Date = new DateTime(2020, 5, 26),
Amount = 1200,
SubPOId=context.SubPOs.First().Id,
Status = new List<string>() { "Open", "Closed", "Pending" }
});
SubPODetailViewModel.cs
<DockPanel Grid.Row="12" Margin="10">
<StackPanel DockPanel.Dock="Right" Width="86">
<Button Content="Add" Margin="10"
Command="{Binding AddInvoiceCommand}"/>
<Button Content="Remove" Margin="10"
Command="{Binding RemoveInvoiceCommand}"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Invoices}"
SelectedItem="{Binding SelectedInvoice,Mode=TwoWay}"
AutoGenerateColumns="False" RowHeaderWidth="0" >
<DataGrid.Columns>
<DataGridTextColumn Header="Invoices" Width="*"
Binding="{Binding InvoiceName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Status" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding Date,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Amount" Width="*" Binding="{Binding Amount,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Status" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Status,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
SubPODetailViewModel.cs(Model)
namespace SubBV.UI.ViewModel
{
public class SubPODetailViewModel : DetailViewModelBase, ISubPODetailViewModel
{
private ISubPORepository _subPORepository;
private IMessageDialogService _messageDialogService;
private SubPOWrapper _subPO;
private SubInvoiceWrapper _selectedInvoice;
public SubPODetailViewModel(IEventAggregator eventAggregator,
IMessageDialogService messageDialogService,
ISubPORepository subPORepository) : base(eventAggregator)
{
_subPORepository = subPORepository;
_messageDialogService = messageDialogService;
AddInvoiceCommand = new DelegateCommand(OnAddInvoiceExecute);
RemoveInvoiceCommand = new DelegateCommand(OnRemoveInvoiceExecute, OnRemoveInvoiceCanExecute);
Invoices = new ObservableCollection<SubInvoiceWrapper>();
}
public SubPOWrapper SubPO
{
get { return _subPO; }
private set
{
_subPO = value;
OnPropertyChanged();
}
}
public SubInvoiceWrapper SelectedInvoice
{
get { return _selectedInvoice; }
set
{
_selectedInvoice = value;
OnPropertyChanged();
((DelegateCommand)RemoveInvoiceCommand).RaiseCanExecuteChanged();
}
}
public ICommand AddInvoiceCommand { get; }
public ICommand RemoveInvoiceCommand { get; }
public ObservableCollection<SubInvoiceWrapper> Invoices { get; }
public override async Task LoadAsync(int? subPOId)
{
var subPO = subPOId.HasValue
? await _subPORepository.GetByIdAsync(subPOId.Value)
: CreateNewSubPO();
InitializeSubInvoice(subPO.Invoices);
}
private void InitializeSubInvoice(ICollection<SubInvoice> invoices)
{
foreach (var wrapper in Invoices)
{
wrapper.PropertyChanged -= SubInvoiceWrapper_PropertyChanged;
}
Invoices.Clear();
foreach (var subInvoice in invoices)
{
var wrapper = new SubInvoiceWrapper(subInvoice);
Invoices.Add(wrapper);
wrapper.PropertyChanged += SubInvoiceWrapper_PropertyChanged;
}
}
private void SubInvoiceWrapper_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubInvoiceWrapper.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
}
private void InitializeSubPO(SubPO subPO)
{
SubPO = new SubPOWrapper(subPO);
SubPO.PropertyChanged += (s, e) =>
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubPO.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
};
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
if (SubPO.Id == 0)
{
// Little trick to trigger the validation
SubPO.Title = "";
}
}
}
SubInvoiceWrapper.cs (Wraps what is contained in the DataGridView)
public class SubInvoiceWrapper:ModelWrapper<SubInvoice>
{
public SubInvoiceWrapper(SubInvoice model) : base(model)
{
}
public string InvoiceName
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
public DateTime Date
{
get { return GetValue<DateTime>(); }
set { SetValue(value); }
}
public decimal Amount
{
get { return GetValue<decimal>(); }
set { SetValue(value); }
}
public List<string> Status
{
get { return GetValue<List<string>>(); }
set { SetValue(value); }
}
}
ViewModelBase.cs (contains the propertychanged)
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
One of my classes
public class CheckedListItem<T> : INotifyPropertyChanged
has the method
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
My XAML looks like
<ListBox ItemsSource="{Binding Employee}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My employee.cs file
public class Employee
{
public string Name { get; set; }
}
I cant figure out how I can write back to the database when a item is checked or unchecked.
I would appreciate any help and would be more than happy to post the entire code for any files you need to see.
You should be able to get what you want by adding TwoWay for mode and PropertyChanged on UpdateSourceTrigger.
Final XAML will then be:
<ListBox ItemsSource="{Binding Employee}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
EDIT:
By the way, I think you might be setting your ItemsSource incorrectly. I'm sure what you want is to bind that listbox to a collection of Employees?
Something like:
public ObservableCollection<Employee> Employees
{
get { return _employees; }
set
{
_employees = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Employees"));
}
}
}
I misunderstood the question so here comes second edit:
Your class files would instead look like this:
public partial class MainWindow : INotifyPropertyChanged
{
private ObservableCollection<MutableKeyValuePair<Employee, bool>> _employees;
public MainWindow()
{
InitializeComponent();
Employees = new ObservableCollection<MutableKeyValuePair<Employee, bool>>
{
new MutableKeyValuePair<Employee, bool>(new Employee("Joakim"), false),
new MutableKeyValuePair<Employee, bool>(new Employee("SoftwareIsFun"), false),
};
}
public ObservableCollection<MutableKeyValuePair<Employee, bool>> Employees
{
get { return _employees; }
set
{
_employees = value;
OnPropertyChanged(nameof(Employees));
}
}
public void UpdateDatabase()
{
foreach (var employee in Employees)
{
if (employee.Value)
{
//DATABASE LOGIC HERE
}
}
}
}
public class MutableKeyValuePair<TKey, TValue>
{
public TKey Key { get; set; }
public TValue Value { get; set; }
public MutableKeyValuePair()
{
}
public MutableKeyValuePair(TKey key, TValue value)
{
Key = key;
Value = value;
}
}
public class Employee
{
public Employee(string name)
{
Name = name;
}
public string Name { get; set; }
}
XAML:
<DataGrid ItemsSource="{Binding Employees}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Key.Name}"/>
<DataGridCheckBoxColumn Binding="{Binding Value}"/>
</DataGrid.Columns>
</DataGrid>
stackoverflow!
Starting with my code:
XAML
<DataGrid Margin="25,112,25,10" Name="datGrid" RowHeight="30"
ColumnWidth="150" BorderThickness="1"
Style="{StaticResource AzureDataGrid}" IsReadOnly="False"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Content="CREATE" Name="headerCheckBox"
FontWeight="Bold" Width="Auto"
Checked="headerCheckBox_Checked"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Username" Binding="{Binding Path=Username}"/>
<DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"/>
<DataGridTextColumn Header="Last Name" Binding="{Binding Path=LastName}"/>
<DataGridTextColumn Header="Email" Binding="{Binding Path=Email}"/>
</DataGrid.Columns>
</DataGrid>
And C#
public partial class MainWindow : Window
{
ObservableCollection<MyData> MyItems = new ObservableCollection<MyData>();
public MainWindow()
{
datGrid.ItemsSource = MyItems;
MyItems.Add(new MyData { IsChecked = false, Username = "apetecca", FirstName = "Anthony", LastName = "Petecca", Email = "apetecca#email.com"});
MyItems.Add(new MyData { IsChecked = true, Username = "jhalls", FirstName = "Jake", LastName = "Halls", Email = "jhalls#email.com" });
}
public class MyData
{
public bool IsChecked { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
private void headerCheckBox_Checked(object sender, RoutedEventArgs e)
{
foreach (var item in MyItems)
{
item.IsChecked = true;
}
}
When I click on the headerCheckBox to check it, I've watched the variables in the foreach loop to see it is changing the items from false to true but doesn't visually show that on the DataGrid. However, if I manually check or uncheck the boxes, it displays correctly when going through that loop. The loop correctly sets the values but does not change the GUI display.
Everything I have seen online indicates it has to do with TwoWay mode and UpdateSourceTrigger. Both of these are set and I can't seem to find anything else on these.
Any help would be greatly appreciated!
EDIT-
I also tried changing my class to look like this
public class MyData
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
This didn't resolve it either.
Implement INotifyPropertyChanged and then have your properties raise event to make this happen.
Something like:
public class MyData : INotifyPropertyChanged
{
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
if (isChecked != value)
{
isChecked = value;
OnPropertyChanged("IsChecked");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I have a ComboBox in my XAML. It is populated with the following code:
PopulateColors.cs
public class PopulateColors
{
public ObservableCollection<ItemsColors> itemsColors { get; set; }
public PopulateColors()
{
this.itemsColors = new ObservableCollection<ItemsColors>();
this.itemsColors.Add(new ItemsColors{ ItemColumn = "Blue", IdItemColumn = 0 });
this.itemsColors.Add(new ItemsColors{ ItemColumn = "Red", IdItemColumn = 1 });
this.itemsColors.Add(new ItemsColors{ ItemColumn = "Pink", IdItemColumn = 2 });
}
}
public class ItemsColors
{
public string ItemColumn { get; set; }
public int IdItemColumn { get; set; }
}
pagedemo.xaml.cs:
ClothingViewModel ClothVM = null;
public pagedemo()
{
this.comboColors.DataContext = new PopulateColors();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ClothVM = new ClothingViewModel();
ClothVM = ClothVM.GetData(1);
this.DataContext = ClothVM ;
navigationHelper.OnNavigatedTo(e);
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
string result = ClothVM .Save( ClothVM );
if (result.Contains("OK"))
{
//to do
}
}
pagedemo.xaml (XAML design)
<TextBox x:Name="txtJersey"
Text="{Binding Jersey, Mode=TwoWay}"/>
<ComboBox Name="comboColors"
ItemsSource="{Binding itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValuePath="ItemColumn"/>
Ok. items are display fine in ComboBox.
QUESTION:
I need to save the selected color in a table of the database, using MVVM pattern. But how? I have this code. But I do not know how to link it with the ComboBox:
Model: Clothing.cs
Public class Clothing
{
public string Color { get; set; }
public string Jersey { get; set; }
}
ViewModel: ClothingViewModel.cs
public class ClothingViewModel : ViewModelBase
{
public string Save (ClothingViewModel cloth)
{
string result = string.Empty;
using (var db = new SQLite.SQLiteConnection(App.DBPath))
{
string change = string.Empty;
try
{
var existing = (db.Table<Clothing>().Where(
c => c.id == 1)).SingleOrDefault();
if (existing!= null)
{
existing.Color = cloth.Color;
existing.Jersey = cloth.Jersey;
int success = db.Update(existing);
}
}
catch
{ }
}
}
private int id = 1;
public int ID
{
get
{ return id; }
set
{
if (id == value)
{ return; }
id= value;
RaisePropertyChanged("ID");
}
}
private string color = string.Empty;
public string Color
{
get
{ return color; }
set
{
if (color == value)
{ return; }
color = value;
isDirty = true;
RaisePropertyChanged("Color");
}
}
private string jersey = string.Empty;
public string Jersey
{
get
{ return jersey; }
set
{
if (jersey == value)
{ return; }
jersey = value;
isDirty = true;
RaisePropertyChanged("Jersey");
}
}
}
Actually, there are plenty of options. Let's demonstrate just a few of them.
1. Use Binding with RelativeSource to find Ancestor with appropriate DataContext
XAML-code:
<!-- Please use Page instead of Window. -->
<Window>
<StackPanel>
<TextBox x:Name="txtJersey"
Text="{Binding Jersey, Mode=TwoWay}"/>
<!-- Use {x:Type Page} instead of {x:Type Window}. -->
<ComboBox Name="comboColors" ItemsSource="{Binding itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValue="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.Color}"
SelectedValuePath="ItemColumn" />
</StackPanel>
</Window>
2. Use Binding with ElementName to the UI-element with appropriate DataContext
XAML-code:
<StackPanel>
<TextBox x:Name="txtJersey"
Text="{Binding Jersey, Mode=TwoWay}"/>
<ComboBox Name="comboColors" ItemsSource="{Binding itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValue="{Binding
ElementName=txtJersey,
Path=DataContext.Color}"
SelectedValuePath="ItemColumn" />
</StackPanel>
3. Use "one" ViewModel that contains another ViewModel
public class ClothingViewModel : ViewModelBase
{
private readonly PopulateColors colors = new PopulateColors();
public PopulateColors Colors
{
get { return this.colors; }
}
...
}
Page:
// This is a Page (Window in case of using WPF).
public class ClothingWindow
{
public ClothingWindow()
{
InitializeComponent();
// Note: no need to set the DataContext for the ComboBox.
DataContext = new ClothingViewModel();
}
}
XAML-code:
<StackPanel>
<TextBox Text="{Binding Jersey, Mode=TwoWay}"/>
<ComboBox ItemsSource="{Binding Colors.itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValue="{Binding Color}"
SelectedValuePath="ItemColumn" />
</StackPanel>
References
Data Binding (WPF), MSDN.
Data Binding in WPF, John Papa, MSDN Magazine.
First datagrid is binded to ObservableCollection from my MainViewModel class. Second datagrid which i want to filter is binded like this:
ICollectionView icv;
icv = CollectionViewSource.GetDefaultView(list1);
icv.Filter = FilterTable;
dataGrid1.ItemsSource = icv;
list1 is same ObservableCollection from first datagrid. With this code my both datagrids were filtered. Is there any way to filter only second datagrid, but not first?
You can use two different view and get this filter done. refer below code.
<StackPanel>
<DataGrid ItemsSource="{Binding PersonsViews}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource="{Binding PersonsFileterViews}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
class MainViewModel
{
private ObservableCollection<Person> perList = new ObservableCollection<Person>();
public ObservableCollection<Person> PersonList
{
get { return perList; }
set { perList = value; }
}
public ICollectionView PersonsViews { get; set; }
public ICollectionView PersonsFileterViews { get; set; }
public MainViewModel()
{
perList.Add(new Person() { Age = 1, Name = "Test1"});
perList.Add(new Person() { Age = 2, Name = "Test2" });
perList.Add(new Person() { Age = 3, Name = "Test3" });
perList.Add(new Person() { Age = 4, Name = "Test4" });
PersonsViews = new CollectionViewSource { Source = PersonList }.View;
PersonsFileterViews = new CollectionViewSource { Source = PersonList }.View;
PersonsFileterViews.Filter = new Predicate<object>(x => ((Person)x).Age > 2);
}
}
public class Person
{
private int age;
public int Age
{
get { return age; }
set { age = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}