Cannot bind slider value in DataGridTemplateColumn - c#

I am trying to create a DataGrid with two text columns and a template column as follows:
<UserControl
...
Name="TcpNtcpSliders"
...>
<DataGrid Name="MinTcpDataGrid" AutoGenerateColumns="False" Margin="2" Grid.Row="0"
ItemsSource="{Binding MinTcpRows, ElementName=TcpNtcpSliders, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns>
<DataGridTextColumn Header="Structure ID" IsReadOnly="True" Width="Auto"
Binding="{Binding StructureId, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Min. TCP" IsReadOnly="True" Width="Auto" x:Name="MinTcpTextColumn"
Binding="{Binding MinTcpValue, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Set Min. Tcp" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Slider Minimum="0" Maximum="1"
Value="{Binding MinTcpValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>
In the code behind I have
internal ObservableCollection<MinTcpRow> MinTcpRows
{
get { return (ObservableCollection<MinTcpRow>)GetValue(MinTcpRowsProperty); }
set { SetValue(MinTcpRowsProperty, value); }
}
internal static readonly DependencyProperty MinTcpRowsProperty =
DependencyProperty.Register("MinTcpRows", typeof(ObservableCollection<MinTcpRow>),
typeof(RtpOptimizationTcpNtcpSliders),
new PropertyMetadata(new ObservableCollection<MinTcpRow>(), MinTcpRowsPropertyChangedCallback));
private static void MinTcpRowsPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (RtpOptimizationTcpNtcpSliders)d;
control.MinTcpRows = (ObservableCollection<MinTcpRow>)e.NewValue;
control.CreateTcpNtcpLimits();
}
and the MinTcpRow class is defined like:
public class MinTcpRow
{
public string StructureId { get; }
public double MinTcpValue { get; set; }
public MinTcpRow(string structureId, double minTcpValue)
{
if (minTcpValue < 0 || minTcpValue > 1)
throw new ArgumentOutOfRangeException($"{nameof(minTcpValue)} must " +
$"be in range [0, 1], but was {minTcpValue}.");
StructureId = structureId ?? throw new ArgumentNullException(nameof(structureId));
MinTcpValue = minTcpValue;
}
}
Everything works as expected, except for the Slider value binding. I says cannot resolve symbol 'MinTcpValue'. How can I bind the slider's value correctly?

In order to see what happens I put a simplified version together.
In my mainwindow I have a datagrid:
<DataGrid Name="MinTcpDataGrid" AutoGenerateColumns="False"
ItemsSource="{Binding Rows}">
<DataGrid.Columns>
<DataGridTextColumn Header="Structure ID" IsReadOnly="True" Width="Auto"
Binding="{Binding StructureId, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Min. TCP" IsReadOnly="True" Width="Auto" x:Name="MinTcpTextColumn"
Binding="{Binding MinTcpValue, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Set Min. Tcp" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Slider Minimum="0" Maximum="1"
Value="{Binding MinTcpValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
MainWindowViewModel will supply my Rows collection:
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<MinTcpRow> rows = new ObservableCollection<MinTcpRow>();
public MainWindowViewModel()
{
rows.Add(new MinTcpRow ("A",0.1));
rows.Add(new MinTcpRow("B", 0.5));
rows.Add(new MinTcpRow("C", 0.9));
}
}
And MinTcpRow ( is faulted because it won't notify change ) but looks the same.
public class MinTcpRow
{
public string StructureId { get; }
public double MinTcpValue { get; set; }
public MinTcpRow(string structureId, double minTcpValue)
{
if (minTcpValue < 0 || minTcpValue > 1)
throw new ArgumentOutOfRangeException($"{nameof(minTcpValue)} must " +
$"be in range [0, 1], but was {minTcpValue}.");
StructureId = structureId ?? throw new ArgumentNullException(nameof(structureId));
MinTcpValue = minTcpValue;
And that works, no error anyhow. There is a memory leak waiting to catch you out because of not implementing inpc but I get no error like you show us. Are you somehow using a different version of MinTcpRow ?

Related

new row in datagrid with custom data

I have two datagrids. The first one is an overview about a time tracking. The second one shows some details.
For example:
The first datagrid contains each day, where a employee works. The second datagrid contains the time stamping for a day.
However, when I add a new stamping row, the controls in the datagrid are empty. That is not really a problem but the datepicker shows the date 01/01/0001.
what i want is when i add a new row, the date field should be filled with the date from the first datagrid.
VIEW:
<DataGrid Grid.Column="0"
IsReadOnly="True"
AutoGenerateColumns="False"
Grid.Row="0"
x:Name="DgStundenView"
Margin="0 0 10 0"
SelectedItem="{Binding SelectedItem}"
ItemsSource="{Binding TimeOverviewList}">
<DataGrid.Columns>
<DataGridTextColumn Header="Wochentag" Binding="{Binding Wochentag}" />
<DataGridTextColumn Header="Datum" Binding="{Binding Tag}" />
<DataGridTextColumn Header="Arbeitszeit Soll" Binding="{Binding ArbeitszeitInStunden}" />
<DataGridTextColumn Header="Arbeitszeit gesamt" Binding="{Binding GesamtDauerInStunden}" />
<DataGridTextColumn Header="Pausenzeit" Binding="{Binding Pausenzeit}" />
<DataGridTextColumn Header="Pausendifferenz" Binding="{Binding PausenDifferenzInStunden}" />
<DataGridTextColumn Header="Arbeitszeit inkl. Pause" Binding="{Binding TatsaechlicheDauerInStunden}" />
<DataGridCheckBoxColumn Header="Status" Binding="{Binding StempelungVorhanden,Mode=OneWay}" />
<DataGridTextColumn Header="Info" Binding="{Binding Info}" />
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Wochentag}" Value="Sa">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
<DataTrigger Binding="{Binding Wochentag}" Value="So">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
<DataGrid Grid.Column="0"
Grid.Row="1"
x:Name="DgStempelungen"
Margin="0 10 10 0"
AutoGenerateColumns="False"
CanUserAddRows="True"
SelectedItem="{Binding SelectedValue}"
ItemsSource="{Binding TimeDetailList}">
<DataGrid.Resources>
<x:Array x:Key="Reasons" Type="system:String">
<system:String>NICHT ANWENDBAR</system:String>
<system:String>KRANK</system:String>
<system:String>GANZER TAG URLAUB</system:String>
</x:Array>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}"/>
<DataGridTemplateColumn Header="KOMMEN DATUM">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding ComeBooking}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="KOMMEN UHRZEIT">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:TimePicker Text="{Binding ComeBookingTime, StringFormat=t}"
Is24Hours="True">
</materialDesign:TimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="GEHEN DATUM">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding GoBooking}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="GEHEN UHRZEIT">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:TimePicker Text="{Binding GoBookingTime, StringFormat=t}"
Is24Hours="True">
</materialDesign:TimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridComboBoxColumn Header="GRUND"
ItemsSource="{StaticResource Reasons}"
TextBinding="{Binding Info}"/>
</DataGrid.Columns>
</DataGrid>
VIEW MODEL
[CanBeNull] private ClassTimeTrackingDayStamp _selectedItem;
[CanBeNull]
public ClassTimeTrackingDayStamp SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
if (_selectedItem != null)
TimeDetailList = new ObservableCollection<ClassTimeTrackingTimeStamp>(_selectedItem.Stempelungen);
}
}
private ObservableCollection<ClassTimeTrackingDayStamp> _timeOverviewList;
public ObservableCollection<ClassTimeTrackingDayStamp> TimeOverviewList
{
get { return _timeOverviewList; }
set
{
_timeOverviewList = value;
OnPropertyChanged();
}
}
[CanBeNull] private ObservableCollection<ClassTimeTrackingTimeStamp> _timeDetailList;
[CanBeNull]
public ObservableCollection<ClassTimeTrackingTimeStamp> TimeDetailList
{
get { return _timeDetailList; }
set
{
_timeDetailList = value;
OnPropertyChanged();
}
}
MODEL
public class ClassTimeTrackingTimeStamp : ViewModelBase
{
public DateTime ComeBooking { get; set; }
public DateTime GoBooking { get; set; }
public TimeSpan ComeBookingTime { get; set; }
public TimeSpan GoBookingTime { get; set; }
public int Id { get; set; }
public string Info { get; set; }
}
Update
EVENT OVER MVVM VIEW
<DataGrid Grid.Column="0"
Grid.Row="1"
x:Name="DgStempelungen"
Margin="0 10 10 0"
AutoGenerateColumns="False"
CanUserAddRows="True"
SelectedItem="{Binding SelectedValue}"
ItemsSource="{Binding TimeDetailList}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AddingNewItem">
<i:InvokeCommandAction Command="{Binding Path=AddNewItemCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
VIEW MODEL
public ICommand AddNewItemCommand { get; set; }
#endregion Properties
#region Events
public event EventHandler AddNewItemEventHandler;
#endregion
#region Constructor
public WTimeClockOverviewVM()
{
AddNewItemCommand = new RelayCommand(o =>
{
this.AddNewItemEventHandler?.Invoke(this, EventArgs.Empty);
});
}
CODE BEHIND
public WTimeClockOverview()
{
InitializeComponent();
var vm = (WTimeClockOverviewVM)DataContext;
if(vm == null) return;
vm.AddNewItemEventHandler += (sender, args) =>
{
var selectedItemInFirstDg = vm.SelectedItem;
if (selectedItemInFirstDg != null)
{
var newItem = new ClassTimeTrackingTimeStamp();
newItem.ComeBooking = selectedItemInFirstDg.Tag;
}
};
}
Thank you for your help :)
Best regards
Levin
In second DataGrid, you can subscribe to AddingNewItem event
<DataGrid x:Name="DgStempelungen"
AddingNewItem="DgStempelungen_OnAddingNewItem"
...
In code behind
private void DgStempelungen_OnAddingNewItem(object sender, AddingNewItemEventArgs e)
{
// I'll assume that the Model's Name in first DataGrid collection is StudentItem!
var selectedItemInFirstDg = DgStundenView.SelectedItem as StudentItem;
// or you can get the selected item from the DataContext:
// (DgStundenView.DataContext as MyViewModel)?.SelectedItem;
if (selectedItemInFirstDg != null){
// create the new row with data from selectedItemInFirstDg, ex. date, etc...
e.NewItem = new ClassTimeTrackingDayStamp
{
ComeBooking = selectedItemInFirstDg.Date; // for example
};
}
}

Set Property of UserControl in DataGrid?

I'm working on a program for playing audio files and have ran into some issues with one of my DataGrids. Basically it's meant to allow the user change settings for it (Listen, repeat and volumes for example).
For this I have made my own UserControl, which is being dynamically added via DataTemplates in XAML. So first of all here's some context;
SoundFile.cs (public class)
Class that has file-related properties such as Name, Path, Category and Volumes.
SoundLibrary.cs (public static)
This is a static class which keeps track of all SoundFiles inside an ObservableCollection, this is the collection used to populate my DataGrids.
SoundControl.cs (UserControl)
This is my UserControl which consists of a few buttons, it has a property for SoundFile which is what I'm confused as to how to set and/or properly access.
This is how my DataGrid is defined;
<DataGrid
x:Name="MyDataGrid"
Margin="0,0,0,36"
AlternationCount="2"
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" />
<DataGridTemplateColumn Header="Listen Volume">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Slider
Margin="5"
Maximum="100"
Minimum="0"
Value="{Binding DataContext.ListenVolume, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Settings">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:SoundControl x:Name="SoundController" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Progress">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ProgressBar
Maximum="100"
Minimum="0"
Visibility="Visible"
Value="50" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And here's how I bind it in code behind;
MyDataGrid.ItemsSource = SoundLibrary.Library;
for (int i = 0; i < MyDataGrid.Items.Count; i++)
{
SoundFile sf = (SoundFile)MyDataGrid.Items.GetItemAt(i);
Debug.WriteLine(sf.FilePath);
}
Doing that I can access the SoundFile as expected, however I'm not sure how to access the SoundFile through the SoundControl.
The SoundControl class is very simple;
public partial class SoundControl : UserControl
{
public SoundFile Sound { get; set; } = new SoundFile();
public SoundControl()
{
InitializeComponent();
ListenButton.IsToggled = Sound.Listen;
RepeatButton.IsToggled = Sound.Repeat;
}
private void GridButton_MouseUp(object sender, MouseButtonEventArgs e)
{
if (sender == ListenButton)
{
Sound.Listen = ListenButton.IsToggled;
MainWindow mainWindow = (MainWindow)Window.GetWindow(this);
mainWindow.UnsavedChanges = true;
}
if (sender == RepeatButton)
{
Sound.Repeat = RepeatButton.IsToggled;
MainWindow mainWindow = (MainWindow)Window.GetWindow(this);
mainWindow.UnsavedChanges = true;
}
if (sender == FolderButton)
{
Debug.WriteLine(Sound.FilePath);
//Sound.OpenDirectory();
}
}
}
Any pointers in the right direction would be much appreciated, still relatively new to data binding and using DataGrids... Thanks in advance!
The Sound property of the SoundControl should be a bindable property, i.e. dependency property:
public static readonly DependencyProperty SoundProperty =
DependencyProperty.Register(
nameof(Sound), typeof(SoundFile), typeof(SoundControl));
public SoundFile Sound
{
get { return (SoundFile)GetValue(SoundProperty); }
set { SetValue(SoundProperty, value); }
}
You would bind it to the current SoundFile item of the DataGrid row like this:
<DataGridTemplateColumn Header="Settings">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:SoundControl Sound="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

How to obtain row index and assign it to observable collection property of datagrid

Basically, I have a DataGrid inside a DataGrid:
<DataGrid ScrollViewer.CanContentScroll="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
AutomationProperties.AutomationId="sCGridSelectedBasketItems"
HeadersVisibility="Row"
Height="{Binding ElementName=itemsgrid,Path=ActualHeight}"
Grid.Row="1"
RowDetailsVisibilityMode="Visible"
CanUserAddRows="false"
VerticalAlignment="Top"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedBasketItem}"
ItemsSource="{Binding SelectedOrderBasketItems}"
CanUserDeleteRows="True">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ScrollViewer.CanContentScroll="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
CanUserAddRows="false"
HeadersVisibility="Row"
AutoGenerateColumns="False"
ItemsSource="{Binding SelectedBasketItems}"
SelectedValue="{Binding SelectedBasketItemValue}"
SelectedIndex="{Binding SelectedOrderItemIndex}"
SelectedItem="{Binding SelectedSpecificItemInBasket, Mode=TwoWay}"
SelectionChanged="datagrid_SelectionChanged"
DataGrid.SelectionMode="Single"
Style="{StaticResource GridItemsControlStyle}"
ctrls:DragDrop.DropHandler="{Binding DropHandler}">
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True"
Binding="{Binding Path=ItemName}"
Width="195">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping"
Value="Wrap" />
<!--<Setter Property="TextBlock.TextAlignment"
Value="Center" />-->
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=RulesCount}"
Background="{Binding Path=RulesCount ,Converter={StaticResource ItemColourConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="115">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=LinkedOrderCount}"
Background="{Binding Path=LinkedOrderCount ,Converter={StaticResource ItemColourConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="55">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=SDICount}">
<Label.Background>
<MultiBinding Converter="{StaticResource SdiItemColourConverter}">
<Binding ElementName="SDIMaxCnt"
Path="Value" />
<Binding Path="SDICount" />
</MultiBinding>
</Label.Background>
</Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
My ObservableCollection has the following fields where I have to bind SelectedItemIndex of each DataGrid row with SelectedOrderItemIndex(which is a property of ObservableCollection class MobileOrderBasketItems.cs):
public int SelectedOrderItemIndex
{
get
{
return selectedOrderItemIndex;
}
set
{
selectedOrderItemIndex = value;
}
}
My Observable collection:
public ObservableCollection<MobileOrderBasketItems> SelectedBasketItems
{
get
{
return _selectedBasketItems ?? (_selectedBasketItems = new ObservableCollection<MobileOrderBasketItems>());
}
set
{
_selectedBasketItems = value;
}
}
My ObservableCollection class definitions:
public class MobileOrderBasketItems
{
private int _sdiMaxCountVal;
private int selectedOrderItemIndex;
public string GrName { get; set; }
public string ItemName { get; set; }
public string RulesCount { get; set; }
public string LinkedOrderCount { get; set; }
public string SDICount { get; set; }
public int SelectedOrderItemIndex
{
get
{
return selectedOrderItemIndex;
}
set
{
selectedOrderItemIndex = value;
}
}
private ObservableCollection<MobileOrderBasketItems> _selectedBasketItems;
public ObservableCollection<MobileOrderBasketItems> SelectedBasketItems
{
get
{
return _selectedBasketItems ?? (_selectedBasketItems = new ObservableCollection<MobileOrderBasketItems>());
}
set
{
_selectedBasketItems = value;
}
}
}
Any help on how to achieve this is much appreciated.

Unable to update Datagrid and save changes to database

I have a datagrid, an ObservableCollection of Product Types in a ViewModel and an implementation of EventToCommand like shown below.
I would like to update the Total Column from the product of Quantity and Cost Column and save the changes without using the evil code behind or Windows Forms DataGridView.
How can I achieve this?
Datagrid:
<DataGrid x:Name="dataGrid" Margin="5,5,10,5" AutoGenerateColumns="False" HorizontalAlignment="Stretch" ItemsSource="{Binding ProductList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Stretch" Height="566" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding" SourceObject="{Binding ElementName=Control}">
<cmd:EventToCommand Command="{Binding EndEdit}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn x:Name="Id" Binding="{Binding Path=Id, Mode=TwoWay}" Header="Id"/>
<DataGridTextColumn x:Name="name" Binding="{Binding Path=Name, Mode=TwoWay}" Header="Name"/>
<DataGridTextColumn x:Name="cost" Binding="{Binding Path=Cost, Mode=TwoWay}" Header="Cost"/>
<DataGridTextColumn x:Name="Quantity" Binding="{Binding Path=Quantity, Mode=TwoWay}" Header="Quantity"/>
<DataGridTextColumn x:Name="total" Binding="{Binding Path=Total, Mode=TwoWay}" Header="Total"/>
</DataGrid.Columns>
Then in the ViewModel
private ObservableCollection<Product> _product;
public ObservableCollection<Product> MyProduct
{
get
{
return _product;
}
set
{
Set(ref _product, value);
}
}
public ProductViewModel(IDataService proxy)
{
_proxy = proxy;
LoadCommand = new RelayCommand(DoGetProducts);
EndEdit = new RelayCommand<DataGridCellEditEndingEventArgs>(DoEndEdit);
}
private void DoEndEdit(DataGridCellEditEndingEventArgs obj)
{
DataGridRow row = obj.Row;
Product p = (Product)row.Item;
p.Total = p.Cost*p.Quantity;
_proxy.SaveAll();
}
Then in the Model:
public class DataService : IDataService
{
ProductEntities context;
public DataService()
{
context = new ProductEntities();
}
public ObservableCollection<Product> GetProducts(){
ObservableCollection<Product> products = new ObservableCollection<Product>();
foreach(var p in context.Products.Tolist()){
products.add(p);
}
return products;
}
public void SaveAll()
{
context.SaveChanges();
}
}
The datagrid is loading products but not updating the Total when Cost and Quantity is changed. Also, not saving the changes in database
For the "total" column in the DataGrid to get updated, the Product class should implement the INotifyPropertyChanged interface and raise the PropertyChanged event for the Total property:
private double _total;
public double Total
{
get { return _total; }
set { _total = value; OnPropertyChanged("Total"); }
}
And for you to be able to save the value to the database, you need to map the Total property against a column in your database table, just like you (hopefully) did with the other columns.
The output will be like this:-
Change your Xaml like give below
<DataGrid x:Name="dataGrid" Margin="5,5,10,5" AutoGenerateColumns="False" HorizontalAlignment="Stretch" ItemsSource="{Binding ProductList}" VerticalAlignment="Stretch" Height="566" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="RowEditEnding" ">
<cmd:EventToCommand Command="{Binding EndEdit}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn x:Name="Id" Binding="{Binding Path=Id, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Header="Id"/>
<DataGridTextColumn x:Name="name" Binding="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Header="Name"/>
<DataGridTextColumn x:Name="cost" Binding="{Binding Path=Cost, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Header="Cost"/>
<DataGridTextColumn x:Name="Quantity" Binding="{Binding Path=Quantity, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Header="Quantity"/>
<DataGridTextColumn x:Name="total" Binding="{Binding Path=Total, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Header="Total"/>
</DataGrid.Columns>
And in View Model you Bindings be like
public BindingList<Product> ProductList
{
get
{
return _proxy.ProductList;
}
}
And EndEdit Command Should execute the following function
private void ExecuteEndEdit(DataGridRowEditEndingEventArgs param)
{
var product = param.Row.Item as Product;
var result = ProductList.FirstOrDefault(p => p.Id == product.Id);
var index= ProductList.IndexOf(result);
result.Total = result.Cost * result.Quantity;
ProductList.ResetItem(index);
}
Your IDataService can Expose Binding List like
public class DataService : IDataService
{
ProductEntities context;
public DataService()
{
context = new ProductEntities();
}
public BindingList<Product> ProductList
{
get
{
//EDIT: YOU HAVE TO CALL context.Products.Load(); OR IT WILL RETURN EMPTY RESULTS
context.Products.Load();
return context.Products.Local.ToBindingList<Product>();
}
}
public void SaveAll()
{
context.SaveChanges();
}
}
The Context.Save will save your code.

combobox with images in datagrid

i want to fill a datagrid with the columns "Datum" (DateTime), "Aktion" (String), "Wer" (string),"BisWann" (DateTime), "Status" (int). The "Status" column should be a dropdown list, which contains four icons and dependent on the int value, there should be the correct image preselected.
how can i archive this? So far, i have the datagrid filled with every value, except the dropdownlist (in the following code just a sample entry w/o database)
Window.xaml:
<DataGrid Name="dgMassnahmen" AutoGenerateColumns="False" Margin="10,10,10,10" Grid.Row="1">
<DataGrid.Columns>
<DataGridTextColumn Header="Datum" Binding="{Binding Datum, StringFormat=\{0:dd.MM.yy\}}"/>
<DataGridTextColumn Header="Aktion" Binding="{Binding Aktion}"/>
<DataGridTextColumn Header="Wer" Binding="{Binding Wer}"/>
<DataGridTemplateColumn Header="BisWann">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding BisWann}" BorderThickness="0" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridComboBoxColumn Header="Status" SelectedValueBinding="{Binding Status}"/>
</DataGrid.Columns>
</DataGrid>
the Itemclass:
public class Massnahmen
{
public DateTime Datum { get; set; }
public string Aktion { get; set; }
public string Wer { get; set; }
public DateTime BisWann { get; set; }
public int Status { get; set; }
}
in Window.xaml.cs:
private void FillMassnahmen()
{
List<Klassen.Massnahmen> massnahmen = new List<Klassen.Massnahmen>();
massnahmen.Add(new Klassen.Massnahmen() { Datum = DateTime.Now, Aktion = "DoSomething", BisWann = DateTime.Now.AddDays(2), Status = 2, Wer = "OfCourseYOU" });
dgMassnahmen.ItemsSource = massnahmen;
}
You could set the ItemsSource to an array of Image elements and set the Tag property of each element to an int value that you bind to the Status property:
<DataGridComboBoxColumn Header="Status"
SelectedValueBinding="{Binding Status}"
SelectedValuePath="Tag"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<DataGridComboBoxColumn.ItemsSource>
<x:Array Type="{x:Type Image}">
<Image Source="1.png">
<Image.Tag>
<s:Int32>1</s:Int32>
</Image.Tag>
</Image>
<Image Source="2.png">
<Image.Tag>
<s:Int32>2</s:Int32>
</Image.Tag>
</Image>
</x:Array>
</DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn>

Categories