EDIT : Working better, but still not perfect :
SelectedFace has been set as ComboElement (still couldn't manage to do the job without this additional parameter).
Still not working : When I create a new Tool, SelectedFace is updated correctly, but if I want to edit an existing one, no Update is made(no binding).
End Edit
I have a GridView, in which there is a ComboBox to select on which face each tool located.
I cannot manage to bind the selected value to my object property.
Here are the properties as declared in my Tool object :
private long face = 0;
public long Face
{
get { return face; }
set
{
this.face = value;
this.NotifyPropertyChanged("Face");
}
}
private ComboElement selectedFace;
public ComboElement SelectedFace
{
get { return selectedFace; }
set
{
this.selectedFace = value;
this.Face = value.MyInt;
this.NotifyPropertyChanged("SelectedFace");
}
}
private ObservableCollection<ComboElement> comboFaceSlot=new ObservableCollection<ComboElement> { new ComboElement { MyInt = 0, MyString = "Web" }, new ComboElement { MyInt = 1, MyString = "Top" }, new ComboElement { MyInt = 2, MyString = "Bottom" }, new ComboElement { MyInt = 3, MyString = "Back" } };
public ObservableCollection<ComboElement> ComboFaceSlot
{
get { return comboFaceSlot; }
set
{
this.comboFaceSlot = value;
this.NotifyPropertyChanged("ComboFaceSlot");
}
}
Nota : At the beginning I had not the SelectedFace property, I added it thinking this would solve my problem, but it didn't.
And XAML is as following :
<DataGrid Grid.Row="1" Margin="5" BorderBrush="{StaticResource WindowBorderColor}" BorderThickness="3" Width="Auto" ItemsSource="{Binding Path=ListTools}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" HorizontalAlignment="Center">
<DataGrid.Columns>
<DataGridTextColumn Header="{x:Static p:Resources.Length}" Binding="{Binding Path=Longueur}" Width="100" />
<DataGridTextColumn Header="{x:Static p:Resources.Diameter}" Binding="{Binding Path=Diameter}" Width="100" />
<DataGridTemplateColumn Header="{x:Static p:Resources.Face}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ComboFaceSlot}" DisplayMemberPath="MyString" SelectedItem="{Binding SelectedFace}" Width="100" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="{x:Static p:Resources.Angle}" Binding="{Binding Path=Angle}" Width="100" />
</DataGrid.Columns>
</DataGrid>
Also forgot, my ComboElement I use for all Comboboxes of my project :
public class ComboElement
{
private long myInt=-1;
public long MyInt
{
get { return myInt; }
set { myInt = value; }
}
private string myString="";
public string MyString
{
get { return myString; }
set { myString = value; }
}
public ComboElement()
{
}
public ComboElement(long id, string name)
{
this.myInt = id;
this.MyString = name;
}
}
Edit : My ViewModel
private ObservableCollection<Tool> listTool = new ObservableCollection<Tool>();
public ObservableCollection<Tool> ListTool
{
get { return listTool; }
set
{
this.listTool = value;
NotifyPropertyChanged("ListTool");
}
}
private Machine cnc = new Machine();
public Machine CNC
{
get { return cnc; }
set { this.cnc = value;NotifyPropertyChanged("CNC"); }
}
public TableTools(Machine cnc)
{
InitializeComponent();
this.CNC = cnc;
foreach(Tool slot in CNC.ListTool)
{
this.ListTool.Add(slot);
}
DataContext = this;
}
It lists a list of Tools, and I want to say on which face each element is located.
But for now something is going wrong, I tried to use several properties of my ComboBox as SelectedItem, SelectedValue, SelectedValuePath, but until now didn't manage to solve it.
Ideally, I would like to delete "SelectedFace" element, and use only "Face" property.
You need to specify the Tool class like such in your question.
The root cause is not specifying the correct SelectedValuePath of your ComboElement object to be mapped to your Tool object's Face property as the SelectedValue.
Tool Class
public class Tool
{
private ObservableCollection<ComboElement> comboFaceSlot = new ObservableCollection<ComboElement> { new ComboElement { MyInt = 0, MyString = "Web" }, new ComboElement { MyInt = 1, MyString = "Top" }, new ComboElement { MyInt = 2, MyString = "Bottom" }, new ComboElement { MyInt = 3, MyString = "Back" } };
public ObservableCollection<ComboElement> ComboFaceSlot
{
get { return comboFaceSlot; }
set
{
this.comboFaceSlot = value;
this.NotifyPropertyChanged("ComboFaceSlot");
}
}
private long face = 0;
public long Face
{
get { return face; }
set
{
this.face = value;
this.NotifyPropertyChanged("Face");
}
}
}
ToolWindow View
<DataGrid Grid.Row="1" Margin="5" BorderBrush="{StaticResource WindowBorderColor}" BorderThickness="3" Width="Auto" ItemsSource="{Binding Path=ListTools}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" HorizontalAlignment="Center">
<DataGrid.Columns>
<DataGridTextColumn Header="{x:Static p:Resources.Length}" Binding="{Binding Path=Longueur}" Width="100" />
<DataGridTextColumn Header="{x:Static p:Resources.Diameter}" Binding="{Binding Path=Diameter}" Width="100" />
<DataGridTemplateColumn Header="{x:Static p:Resources.Face}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ComboFaceSlot}"
DisplayMemberPath="MyString"
SelectedValuePath="MyInt"
SelectedValue="{Binding Face, UpdateSourceTrigger=PropertyChanged}" Width="100" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="{x:Static p:Resources.Angle}" Binding="{Binding Path=Angle}" Width="100" />
</DataGrid.Columns>
Related
I am trying to bind rectangle width property to a variable so with each added record to Data Grid the width of rectangle is different (chart style). I cannot even access this rectangle with x:Name.
XAML:
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Data}" Header="Data" ></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Path=Pamaina}" Header="Pamaina"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Path=TyrimoVieta}" Header="Tyrimo vieta"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Path=Koncentracija}" Header="Koncentacija"></DataGridTextColumn>
<DataGridTemplateColumn Header="Rect">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel x:Name="assTR">
<Rectangle Width="{Binding ilgis}" Height="10" Fill="Green" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
C#
List<DatagridItems> datagridItems = new List<DatagridItems>();
public class DatagridItems
{
public string Data { get; set; }
public string Pamaina { get; set; }
public string TyrimoVieta { get; set; }
public string Koncentracija { get; set; }
}
public List<DatagridItems> LoadCollectionData(string data, string pamaina, string tyrimovieta, string koncentracija)
{
Datagrid_1.Items.Refresh(); //du refresh kad atsinaujintu kolekcija
datagridItems.Add(new DatagridItems()
{
Data = data,
Pamaina = pamaina,
TyrimoVieta = tyrimovieta,
Koncentracija = koncentracija,
});
Datagrid_1.Items.Refresh();
return datagridItems;
}
Call to populate Data Grid
Datagrid_1.ItemsSource = LoadCollectionData(datosFormatas, row["pamaina"].ToString(), tyrimoVieta_isFunkcijos, koncentracija);
I do not actually know if this will even do what I want. Below is the picture what im trying to achieve. I want value in "Koncentracija" to set width of green rectangle. Can this even be done? I managed to draw static width rectangle only.
Any help is appreciated. Image of what i am trying to achieve
I want value in "Koncentracija" to set width of green rectangle. Can this even be done?
Yes it can be done.
Basically you want to bind a string type variable with a variable that is gonna represent the width of your rectangle. How?
Usually width is represented in numeric values ;)
So the simplest solution - you need to change the datatype of Koncentracija property to be int or decimal as #thatguy pointed out. But to keep things simple without using converters - you have to provide a mechanism to notify your view that property has changed.
The simplest way to do that is to implement INotifyPropertyChanged interface and invoke the propertychanged event in the setter with the name of the property like so:
public class DataGridViewItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public string Data { get; set; }
public string Pamaina { get; set; }
public string TyrimoVieta { get; set; }
private int koncentracija;
public int Koncentracija
{
get { return koncentracija; }
set
{
koncentracija = value;
RaisePropertyChanged(nameof(Koncentracija));
}
}
}
I made a simple view to represent adding an element to DataGrid which changes the width of a rectangle by clicking a button. Here is the View code:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DataGrid Name="MyDataGrid" HorizontalAlignment="Left" Height="150" Margin="44,74,0,0" VerticalAlignment="Top" Width="171"/>
<Rectangle Name="MyRectangle" Fill="Green" HorizontalAlignment="Left" Width="{Binding Koncentracija}" Height="47" Margin="350,91,0,0" Stroke="Black" VerticalAlignment="Top" />
<Button Name="AddItemBtn" Content="AddToDataGrid" Margin="63,281,615.6,0" VerticalAlignment="Top" Width="115" Height="53" FontSize="16" Click="AddItemBtn_Click"/>
</Grid>
Now in the code behind your view (in my example "MainWindow.xaml.cs") there's a simple logic which you can adjust to your problem. Here I increment the Koncentracija value by 30, but ofc you can do that by getting the data from your desired object.
The code:
public partial class MainWindow : Window
{
DataGridViewItem dataItem = new DataGridViewItem();
List<Person> people = new List<Person>() { new Person { Name = "Mark", Age = 12 }, new Person { Name = "Chris", Age = 22 } };
public MainWindow()
{
InitializeComponent();
MyDataGrid.ItemsSource = people;
DataContext = dataItem;
}
private void AddItemBtn_Click(object sender, RoutedEventArgs e)
{
people.Add(new Person() { Name = "Tom", Age = 27 });
dataItem.Koncentracija += 30;
MyDataGrid.Items.Refresh();
}
}
Here's a picture how it looks https://ibb.co/B4FCD6S
Hope it helps :)
*Edit: I recommend you to get familiar with MVVM pattern when creating WPF apps as it will help you a lot. There is a tutorial on this here: https://www.tutorialspoint.com/mvvm/index.htm
You need to create a custom DataGridTemplateColumn. You can put whatever you want inside of the DataTemplate.
This works:
MainWindow.xaml
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}">
</DataGridTextColumn>
<DataGridTemplateColumn Header="Width">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="10" Fill="Blue" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
MainWindowVM.cs
using GalaSoft.MvvmLight;
using System.Collections.Generic;
namespace WpfApp1
{
public class MainWindowVM : ObservableObject
{
private List<Item> _Items;
public List<Item> Items
{
get { return _Items; }
set
{
if (value != _Items)
{
_Items = value;
RaisePropertyChanged();
}
}
}
public MainWindowVM()
{
Items = new List<Item>()
{
new Item() {Name = "one", Width = 30.5 },
new Item() { Name = "two", Width = 10.2 }
};
}
}
public class Item : ObservableObject
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
{
_Name = value;
RaisePropertyChanged();
}
}
}
private double _Width;
public double Width
{
get { return _Width; }
set
{
if (value != _Width)
{
_Width = value;
RaisePropertyChanged();
}
}
}
}
}
Result:
Ideally, the Koncentracija property should be of type double and not string, but for that you would need to create a custom data column that provides support for numeric types and also validation to prevent invalid values, an attached beahvior or a conversion between types. You can find solutions to that in this post.
This would also allow you to simply bind the Koncentracija property to the Width of the Rectangle and it would work in your example. You could align the bar graph left with HorizontalAlignment.
<Rectangle HorizontalAlignment="Left" Width="{Binding Koncentracija}" Height="10" Fill="Green" />
If you want to leave your code as it is, you could create a one-way converter for Koncentracija.
public class StringToDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double.TryParse((string)value, out var result);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
You would then create an instance of this converter for the DataGrid and bind the Width property to Koncentracija with the converter that automatically converts the string to double.
<DataGrid ...>
<DataGrid.Resources>
<local:StringToDoubleConverter x:Key="StringToDoubleConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Data}" Header="Data" ></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Path=Pamaina}" Header="Pamaina"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Path=TyrimoVieta}" Header="Tyrimo vieta"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Path=Koncentracija}" Header="Koncentacija"></DataGridTextColumn>
<DataGridTemplateColumn Header="Rect">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel x:Name="assTR">
<Rectangle HorizontalAlignment="Left" Width="{Binding Koncentracija, Converter={StaticResource StringToDoubleConverter}}" Height="10" Fill="Green" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
As a general note, you should implement INotifyPropertyChanged in your view model, otherwise changes to the properties at runtime will not be reflected in the user interface.
I am developing a wpf application and i have a "buyer" named datagrid and i want access row values when a checkbox is checkedI have read some questions on stackoverflow but all went over my head, i was not able to understand them as amatuer yet :( Here is my datagrid xaml code:-
<DataGrid x:Name="buyer" SelectionMode="Single" HorizontalAlignment="Left" SelectionUnit="FullRow" VerticalAlignment="Top" Height="550" Width="992" HorizontalScrollBarVisibility="Visible" IsReadOnly="True" AutoGenerateColumns="False" FrozenColumnCount="1" Margin="0,45,0,0" SelectionChanged="RowFocus" TargetUpdated="buyer_TargetUpdated">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Joining" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="ID" Binding="{Binding buy_id}"/>
<DataGridTextColumn Header="Name" Binding="{Binding bname}"/>
<DataGridTextColumn Header="Number" Binding="{Binding mobileno}"/>
</DataGrid.Columns>
</DataGrid>
I have a button on the same window, which on clicking should give me values from the rows where the CheckBox is checked
Edit: Currently, I am checking if the CheckBox is working by writing in console. Also the CheckBox should be the 0th column, right? But when I print it in the console I get the value of the next column i.e. ID, I used to print the value by putting the following code :-
private void Button_Click_3(object sender, RoutedEventArgs e)
{
/* int i = 0;
Console.WriteLine("hey");
foreach (var item in buyer.Items)
{
string s = (buyer.Items[i] as DataRowView).Row.ItemArray[0].ToString();
if (i==0)
{
Console.WriteLine(s);
var row = buyer.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
}
i++;
}*/
if (buyer.SelectedItems.Count > 0)
{
for (int i = 0; i < buyer.SelectedItems.Count; i++)
{
System.Data.DataRowView selectedFile = (System.Data.DataRowView)buyer.SelectedItems[i];
string str = Convert.ToString(selectedFile.Row.ItemArray[0]);
Console.WriteLine(str);
}
}
}
I used both commented and uncommented code
Try this.... (using RelayCommand found here http://www.kellydun.com/wpf-relaycommand-with-parameter/)
public class BasePropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
ViewModel.....
class Base_ViewModel : BasePropertyChanged
{
public RelayCommand<ObservableCollection<buyer>> ButtonClickCommand { get; set; }
private ObservableCollection<buyer> _buyer;
public ObservableCollection<buyer> buyer
{
get { return _buyer; }
set { _buyer = value; }
}
public Base_ViewModel()
{
ButtonClickCommand = new RelayCommand<ObservableCollection<buyer>>(OnButtonClickCommand);
buyer = new ObservableCollection<ViewModels.buyer>();
buyer.Add(new buyer() { buy_id = 1, bname = "John Doe", mobileno = "" });
buyer.Add(new buyer() { buy_id = 1, bname = "Jane Doe", mobileno = "" });
buyer.Add(new buyer() { buy_id = 1, bname = "Fred Doe", mobileno = "" });
buyer.Add(new buyer() { buy_id = 1, bname = "Sam Doe", mobileno = "" });
}
private void OnButtonClickCommand(ObservableCollection<buyer> obj)
{ // put a break-point here and obj will be the List of Buyer that you can then step though
}
}
Buyer Class.....
public class buyer : BasePropertyChanged
{
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set { _IsSelected = value; }
}
private string _bname;
public string bname
{
get { return _bname; }
set { _bname = value; NotifyPropertyChanged("bname"); }
}
private int _buy_id;
public int buy_id
{
get { return _buy_id; }
set { _buy_id = value; NotifyPropertyChanged("buy_id"); }
}
private string _mobileno;
public string mobileno
{
get { return _mobileno; }
set { _mobileno = value; NotifyPropertyChanged("mobileno"); }
}
}
XAML.....
<StackPanel>
<DataGrid x:Name="buyer" ItemsSource="{Binding buyer}" SelectionMode="Single" HorizontalAlignment="Left" SelectionUnit="FullRow" IsReadOnly="True" AutoGenerateColumns="False" FrozenColumnCount="1" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Joining" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="ID" Binding="{Binding buy_id}"/>
<DataGridTextColumn Header="Name" Binding="{Binding bname}"/>
<DataGridTextColumn Header="Number" Binding="{Binding mobileno}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Button" Command="{Binding ButtonClickCommand}" CommandParameter="{Binding ElementName=buyer, Path=ItemsSource}" Margin="0,202,0,0"></Button>
</StackPanel>
And don't forget to set your DataContext in the View code-behind....
this.DataContext = new Base_ViewModel();
I want to be able to choose either "true" or "false"(boolean) from a ComboBox that is within a wpf Datagrid and be able to save that choice to my database.
I want to be able to indicate inside of a column if the row is "Active" or not through a boolean variable saved to my database as a bit(1 = true; 0 = false).
Here is my create table statement:
Create Table Statement(AccountType)
I populate the datagrid using an ObservableCollection as follows:
protected override void Get()
{
using (var dbContext = new IEMASEntitiesDataContext())
{
var accountType = from s in dbContext.AccountTypes select s;
var observable = new ObservableCollection<AccountType>(accountType);
Collection = observable;
}
}
My XAML code is as follows:
<DockPanel DataContext="{StaticResource ResourceKey=AccountTypeViewModel}" LastChildFill="True">
<ToolBar DockPanel.Dock="Top">
<Button Content="Display" Command="{Binding GetCommand}" Width="78"/>
<Button Content="Save" Command="{Binding SaveCommand}" Width="78"/>
</ToolBar>
<Grid>
<GroupBox x:Name="AccountTypeGroupBox">
<DataGrid x:Name="DataGridAccountType" ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="AccountType" Width="150" Binding="{Binding Path=AccountTypeName, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridComboBoxColumn Header="Active" Width="100" SelectedValueBinding="{Binding StatusList, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="AccountTypeId" DisplayMemberPath="Active"/>
<DataGridTemplateColumn Width="50" Header="Delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnDelete" Content="Delete" Command="{Binding DeleteCommand}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</Grid>
</DockPanel>
It doesn't work. Nothing displays within the comboboxes, and I don't know how to save the selected item to the SQL Server database when it does display. I'd appreciate some help, please. I am using LINQ TO SQL. I am a newbie :-(.
As I can understand the problem is how to bind some XAML resource as a combo ItemsSource and in addtion how to binnd the selected value of a combo to the model behind the DataGrid row.
1. List item:
<Window x:Class="SoDataGridProjectsHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:soDataGridProjectsHelpAttempt="clr-namespace:SoDataGridProjectsHelpAttempt"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<x:Array x:Key="CountriesArray" Type="soDataGridProjectsHelpAttempt:Country">
<soDataGridProjectsHelpAttempt:Country CountryName="Germany" CountryPop="150k"/>
<soDataGridProjectsHelpAttempt:Country CountryName="France" CountryPop="125k"/>
<soDataGridProjectsHelpAttempt:Country CountryName="Belarus" CountryPop="165k"/>
</x:Array>
<x:Array x:Key="StatusArray" Type="soDataGridProjectsHelpAttempt:ActivityStatus">
<soDataGridProjectsHelpAttempt:ActivityStatus VerbalStatus="Yes" BoolStatus="True"/>
<soDataGridProjectsHelpAttempt:ActivityStatus VerbalStatus="No" BoolStatus="False"/>
</x:Array>
</Window.Resources>
<Window.DataContext>
<soDataGridProjectsHelpAttempt:DataGridMainDataContext/>
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding Collection}" AutoGenerateColumns="False" CanUserAddRows="True">
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" Binding="{Binding UName}"/>
<DataGridComboBoxColumn Header="Country" DisplayMemberPath="CountryName"
ItemsSource="{StaticResource CountriesArray}" Width="Auto"
SelectedItemBinding="{Binding CountryData}"/>
<!--<DataGridComboBoxColumn Header="ActivityStatus" Width="Auto" ItemsSource="{StaticResource StatusArray}"
SelectedValueBinding="{Binding IsActive}" SelectedValuePath="BoolStatus" DisplayMemberPath="VerbalStatus"/>-->
<DataGridComboBoxColumn Header="ActivityStatus" SelectedItemBinding="{Binding IsActive, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<DataGridComboBoxColumn.ItemsSource>
<x:Array Type="system:Boolean">
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
</x:Array>
</DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
DataGrid viewmodel:
public class DataGridMainDataContext
{
public DataGridMainDataContext()
{
Collection = new ObservableCollection(new List
{
new UserData
{
UName = "Greg",
IsActive = false,
},
new UserData
{
UName = "Joe",
IsActive = false,
},
new UserData
{
UName = "Iv",
IsActive = false,
}
});
}
public ObservableCollection<UserData> Collection { get; set; }
}
Models:
public class UserData : BaseObservableObject
{
private string _uName;
private object _countryData;
private bool _isActive;
public bool IsActive
{
get { return _isActive; }
set
{
_isActive = value;
OnPropertyChanged();
}
}
public string UName
{
get { return _uName; }
set
{
_uName = value;
OnPropertyChanged();
}
}
public object CountryData
{
get { return _countryData; }
set
{
_countryData = value;
OnPropertyChanged();
}
}
}
public class ActivityStatus:BaseObservableObject
{
private bool _boolStatus;
private string _verbalStatus;
public bool BoolStatus
{
get { return _boolStatus; }
set
{
_boolStatus = value;
OnPropertyChanged();
}
}
public string VerbalStatus
{
get { return _verbalStatus; }
set
{
_verbalStatus = value;
OnPropertyChanged();
}
}
}
public class Country : BaseObservableObject
{
private string _countryName;
private string _countryPop;
public string CountryName
{
get { return _countryName; }
set
{
_countryName = value;
OnPropertyChanged();
}
}
public string CountryPop
{
get { return _countryPop; }
set
{
_countryPop = value;
OnPropertyChanged();
}
}
public Country() { }
public Country(string n, string d)
{
this.CountryName = n;
this.CountryPop = d;
}
}
Hope it will help you.
regards,
I m trying to filter a dataGrid by selecting two values from two comboBoxes, but the values selected send value 0 to the property.
var idShop = Shop; equal to zero
var idSupplier = Supplier; equal to zero
ViewModel
public class ConsultInvoiceViewModel:ViewModelBase
{
public Context ctx = new tContext();
private ICollectionView _dataGridCollection;
private string _filterString;
private ObservableCollection<Invoice> invoiceCollection = new ObservableCollection<Invoice>();
public ConsultInvoiceViewModel()
{
DataGridCollection = CollectionViewSource.GetDefaultView(Get());
//DataGridCollection.Filter = new Predicate<object>(Filter);
}
public ICollectionView DataGridCollection
{
get
{
return _dataGridCollection;
}
set
{
_dataGridCollection = value;
OnPropertyChanged("DataGridCollection"); }
}
private void FilterCollection()
{
if (_dataGridCollection != null)
{
_dataGridCollection.Refresh();
}
}
private void Search()
{
var idShop = Shop;
var idSupplier = Supplier;
var inv = (from i in ctx.Invoices
where i.shop == idShop
&& i.supplier == idSupplier
select i).SingleOrDefault();
invoiceCollection.Clear();
invoiceCollection.Add(inv);
FilterCollection();
}
private ObservableCollection<Invoice> Get()
{
ctx.Invoices.ToList().ForEach(invoice => ctx.Invoices.Local.Add(invoice));
invoiceCollection = ctx.Invoices.Local;
return invoiceCollection;
}
private void GetShop()
{
ctx.shops.ToList().ForEach(shop => ctx.shops.Local.Add(shop));
SShop = ctx.shops.Local;
}
private void GetSupplier()
{
ctx.foodSuppliers.ToList().ForEach(supplier => ctx.foodSuppliers.Local.Add(supplier));
FoodSupplier = ctx.foodSuppliers.Local;
}
private IList<foodSupplier> supplier;
public IList<foodSupplier> FoodSupplier
{
get
{
if (supplier == null)
GetSupplier();
return supplier;
}
set
{
supplier = value;
OnPropertyChanged("FoodSupplier");
}
}
private IList<shop> shop;
public IList<shop> SShop
{
get
{
if(shop == null)
GetShop();
return shop;
}
set
{
shop = value;
OnPropertyChanged("SShop");
}
}
private int _shop;
public int Shop
{
get
{
return _shop;
}
set
{
_shop = value;
OnPropertyChanged("Shop");
}
}
private int _supplier;
public int Supplier
{
get
{
return _supplier;
}
set
{
_supplier = value;
OnPropertyChanged("Supplier");
}
}
#region "Command"
private ICommand searchCommand;
public ICommand SearchCommand
{
get
{
return searchCommand ?? (searchCommand = new RelayCommand(p => this.Search(), p => this.CanSearch()));
}
}
private bool CanSearch()
{
if (Supplier != 0 && Shop != 0)
return true;
else
return false;
}
#endregion
}
View
<StackPanel Orientation="Horizontal">
<Label Content="Shop"/>
<ComboBox Name="shopComboBox"
Margin="5"
ItemsSource="{Binding SShop}"
DisplayMemberPath="shop1" Width="73"
SelectedItem="{Binding Shop, Mode=OneWayToSource}"
SelectedValuePath="idshop" SelectionChanged="shopComboBox_SelectionChanged"/>
<Label Content="Supplier"/>
<ComboBox Name="supplierComboBox"
Margin="5"
ItemsSource="{Binding FoodSupplier}"
DisplayMemberPath="supplier"
SelectedItem="{Binding Supplier, Mode=OneWayToSource}"
SelectedValuePath="idfoodSupplier"
Width="71"/>
<Label Content="Shop"/>
<Button Content="Search"
Margin="5"
Command="{Binding SearchCommand}"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<DataGrid Margin="5" ItemsSource="{Binding DataGridCollection}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="SuppNb" Binding="{Binding suppInvNumber}" Width="*"/>
<DataGridTextColumn Header="Shop" Binding="{Binding shop1.shop1}" Width="*"/>
<DataGridTextColumn Header="Date" Binding="{Binding date}" Width="*"/>
<DataGridTextColumn Header="Supplier" Binding="{Binding foodSupplier.supplier}" Width="*"/>
<DataGridTextColumn Header="Ref Supplier" Binding="{Binding refSupp}" Width="*"/>
<DataGridTextColumn Header="Unit" Binding="{Binding unit}" Width="*"/>
<DataGridTextColumn Header="Quantity" Binding="{Binding quantity}" Width="*"/>
<DataGridTextColumn Header="Prix/MOQ" Binding="{Binding unitPrice}" Width="*"/>
<DataGridTextColumn Header="Total Price" Binding="{Binding totalPrice}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
It's wrong to bind at SelectedItem. Use SelectedValue with SelectedValuePath instead.
SelectedItem can only be binded to an item (object).
While SelectedValue can be binded to the value specified by the SelectedValuePath of the item.
Just change it like below and you should be able to get your result from the combobox.
<ComboBox Name="supplierComboBox"
Margin="5"
ItemsSource="{Binding FoodSupplier}"
DisplayMemberPath="supplier"
SelectedValue="{Binding Supplier, Mode=OneWayToSource}"
SelectedValuePath="idfoodSupplier"
Width="71"/>
You need to either bind Comoboxes to some command in your ViewModel, so that your ViewModel knows what you have selected ?
Or, you need to pass ComboBoxes selected items as parameters to Search command, so that in your Search method you can access User selected values.
Right now you are accessing Shop directly, which is meaningless. You are assuming that when you select an item in ShopComboBox, Shop variable in ViewModel will get that value. This is wrong.
When you bind say an ObservableCollection to DataGrid, and you select a row in DataGrid, ObservableCollection doesn't know anything about this selection but your DataGrid does.
I have a ComboBox that holds a list of StrategyViewModels. The StrategyViewModel has an ObservableCollection of StrategyParameterViewModels inside of it. I have a StrategyViewModel called SelectedStrategy that I bound to the SelectedItem property on the combobox. When the user selects a Strategy from the ComboBox I would like to set the itemsource of a datagrid to the StrategyParameters inside that Strategy. I've tried all different ways, but nothing seems to work.
Here's the XAML:
<ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" ItemsSource="{Binding Strategies}" DisplayMemberPath="Name" SelectedItem="{Binding Path=SelectedStrategy,Mode=TwoWay}" />
<DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings" ItemsSource="{Binding SelectedStrategy.Parameters, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
Here is the Strategy ViewModel:
public class StrategyViewModel : ViewModelBase
{
public StrategyObject Strategy { get; set; }
public int Id
{
get { return Strategy.Id; }
set
{
if (value == Strategy.Id)
return;
Strategy.Id = value;
OnPropertyChanged("Id");
}
}
public string Name
{
get { return Strategy.Name; }
set
{
if (value == Strategy.Name)
return;
Strategy.Name = value;
OnPropertyChanged("Name");
}
}
public ObservableCollection<StrategyParameterViewModel> Parameters { get { return _parameters; } }
public ObservableCollection<StrategyParameterViewModel> _parameters;
public StrategyViewModel()
{
Strategy = new StrategyObject();
_parameters = new ObservableCollection<StrategyParameterViewModel>();
}
public StrategyViewModel(StrategyObject o, IEnumerable<StrategyParameterObject> pms)
{
Strategy = o;
_parameters = new ObservableCollection<StrategyParameterViewModel>();
foreach (StrategyParameterObject s in pms)
{
_parameters.Add(new StrategyParameterViewModel(s));
}
}
}
And here is the StrategyParameter ViewModel:
public class StrategyParameterViewModel : ViewModelBase
{
public StrategyParameterObject StrategyParameter { get; set; }
public int Id
{
get { return StrategyParameter.Id; }
set
{
if (value == StrategyParameter.Id)
return;
StrategyParameter.Id = value;
OnPropertyChanged("Id");
}
}
public int StrategyId
{
get { return StrategyParameter.StrategyId; }
set
{
if (value == StrategyParameter.StrategyId)
return;
StrategyParameter.StrategyId = value;
OnPropertyChanged("StrategyId");
}
}
public string Name
{
get { return StrategyParameter.Name; }
set
{
if (value == StrategyParameter.Name)
return;
StrategyParameter.Name = value;
OnPropertyChanged("Name");
}
}
public string Value
{
get { return StrategyParameter.Value; }
set
{
if (value == StrategyParameter.Value)
return;
StrategyParameter.Value = value;
OnPropertyChanged("Value");
}
}
public StrategyParameterViewModel()
{
StrategyParameter = new StrategyParameterObject();
}
public StrategyParameterViewModel(StrategyParameterObject o)
{
StrategyParameter = o;
}
}
The problem is that you are trying to set up a two way binding with a read-only property. You need to either add a setter for SelectedStrategy.Parameters, or use a OneWay binding on the datagrid itemssource.
Off the top of my head, I don't think you're doing anything wrong with regards to your XAML.
Data Binding is notoriously tricky. I suggest adding PresentationTraceSources to get more debug information as to what is being found. This will ship several lines of data about binding results to your Output window.
<ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" DisplayMemberPath="Name">
<ComboBox.ItemsSource>
<Binding Path="Strategies" PresentationTraceSources.TraceLevel="High"/>
</ComboBox.ItemsSource>
<ComboBox.SelectedItem>
<Binding Path="SelectedStrategy" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
</ComboBox.SelectedItem>
</ComboBox>
<DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings">
<DataGrid.ItemsSource>
<Binding Path="SelectedStrategy.Parameters" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
In addition, WPF Inspector can help, as you can tweak Data Binding expressions in real time when running your actual app. http://wpfinspector.codeplex.com/