WPF DataGrid binding problem - c#

Lets say we have the following code in XAML (the datagrid is bound to an ObservableCollection and the column to a property of the ObservableCollection:
<WpfToolkit:DataGrid
ItemsSource="{Binding Path=Collection}"
HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended"
CanUserAddRows="False" CanUserDeleteRows="False"
CanUserResizeRows="False" CanUserSortColumns="False"
AutoGenerateColumns="False"
RowHeaderWidth="17" RowHeight="25">
<WpfToolkit:DataGrid.Columns>
<WpfToolkit:DataGridTextColumn
Header="Names" Width="2*"
Binding="{Binding Path=Name}"/>
</WpfToolkit:DataGrid.Columns>
</WpfToolkit:DataGrid>
How can you create a new column programmatically in C# with the binding set to a certain PropertyPath (in my case a property of an ObservableCollection)?
This is what I have right now:
Binding items = new Binding();
PropertyPath path = new PropertyPath("Name");
items.Path = path;
MyDataGrid.Columns.Add(new DataGridTextColumn()
{
Header = "Names",
Width = 275,
Binding = items
});
I am pretty sure that the problem is in the PropertyPath but I do not know what I must write in it...
Thank you for any help!

I have almost the exact same code as you, I just create the binding in a slightly different way:
void Add(ColumnViewModel columnViewModel)
{
var column = new DataGridTextColumn
{
Header = columnViewModel.Name,
Binding = new Binding("[" + columnViewModel.Name + "]")
};
dataGrid.Columns.Add(column);
}

I do this in my program.
I use another datagrid and MVVM, but the idea should be the same.
Create a collection that holds all the columns you need, and just bind this collection to the grid in xaml.
So don't define columns in xaml in this case, only in code.
something like this:
<WpfToolkit:DataGrid
ItemsSource="{Binding Path=Collection}"
HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended"
CanUserAddRows="False" CanUserDeleteRows="False"
CanUserResizeRows="False" CanUserSortColumns="False"
AutoGenerateColumns="False"
RowHeaderWidth="17" RowHeight="25"
Columns="{Binding Path=ColumnCollection}"
/>

The reason my code was not working is that I was not writing the good property for the path.
Thank you anyway for the suggestions!

Related

How do I create a datagrid with a string field and a combobox in one of the columns

I have created a wpf application and I am calling a window to display database table fieldnames. I am currently using a datagrid to display the field names. I would like the second column of the datagrid to be a combobox with the items "Include" and "Exclude". I know how to do the datagrid and display the field names. I cannot figure out how to do this. I do not know where to go from here.
My XML so far looks like this:
<DataGrid HeadersVisibility="Column" IsReadOnly="True" ItemsSource="{Binding}" Name="dtGrid" Loaded="GridLoaded" Width="283" HorizontalAlignment="Left" VerticalAlignment="Top" Height="365" Margin="54,74,0,0" BorderThickness="1" BorderBrush="Black">
<DataGrid.Columns>
<DataGridTextColumn x:Name="FieldName" Header="Field Name" Width="180" />
<DataGridComboBoxColumn x:Name="ComboBoxColumn" Width="83" Header="Include Field" SelectedItemBinding="{Binding strFieldInclude}" />
</DataGrid.Columns>
</DataGrid>
My code attempt looks like this:
private void DisplayFields(string[] stringFieldNames)
{
int intDisplayCnt, intTotalRowSize = 0;
string strData, strFieldSize = "";
DataTable dt = new DataTable();
dt.Columns.Add("FieldName", typeof(string));
//DataTable dt = (DataTable)dtGrid.ItemsSource;
for (intDisplayCnt = 0; intDisplayCnt < stringFieldNames.Length; intDisplayCnt++)
{
dt.Rows.Add(stringFieldNames[intDisplayCnt]);
}
dtGrid.ItemsSource = dt.DefaultView;
strFieldIncludeList = new ObservableCollection<string>() { "Include", "Exclude" };
ComboBoxColumn.ItemsSource = strFieldIncludeList;
}

In MahApps Metro DataGridCheckBoxColumn how can I programmatically return checkbox values? (and fix extra row)

I know this isn't necessarily specific to MahApps Metro DataGridCheckBoxColumns, but I figured that information may help someone answer my question.
I'm trying to do two things with my DataGridCheckBox Column in MahApps Metro
1. I would like to have two separate datagrids, and have the ability to return the value in the second column of whichever row is selected
For example, if I had a datagrid that looked like this:
When someone has the checkbox associated with 2 selected, and the checkbox associated with Red selected, I would like a messagebox to show "2 Red".
my .xaml
<DataGrid Name="numberGrid"
ItemsSource="{Binding Path=numberGrid}"
Grid.Row="0"
AutoGenerateColumns="False"
Width="300"
Height="auto">
<DataGrid.Columns>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Header="Select"
Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected, Mode=OneWay}"
/>
<DataGridTextColumn Header="Numbers"
Binding="{Binding Number, Mode=OneWay}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid Name="colorGrid"
ItemsSource="{Binding Path=colorGrid}"
Grid.Row="0"
AutoGenerateColumns="False"
Width="300">
<DataGrid.Columns>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Header="Select"
Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected, Mode=OneWay}"
/>
<DataGridTextColumn Header="Colors"
Binding="{Binding Color, Mode=OneWay}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
my .cs with the pseudocode of what I think should happen commented out.
ObservableCollection<MahAppsNumbers> MAnumbers = new ObservableCollection<MahAppsNumbers>
{
new MahAppsNumbers{Number = "1"},
new MahAppsNumbers{Number = "2"},
new MahAppsNumbers{Number = "3"},
new MahAppsNumbers{Number = "4"}
};
public class MahAppsNumbers
{
public string Number { set; get; }
}
ObservableCollection<MahAppsAccents> MAcolors = new ObservableCollection<MahAppsColors>
{
new MahAppsColors{Color = "Red"},
new MahAppsColors{Color = "Orange"},
new MahAppsColors{Color = "Yellow"},
new MahAppsColors{Color = "Green"}
};
public class MahAppsColors
{
public string Color { set; get; }
}
public myWindow()
{
InitializeComponent();
numberGrid.ItemsSource = MAnumbers;
colorGrid.ItemsSource = MAcolors;
//Something like this maybe? I know this isn't a thing you can do
//string currentNumber = numberGrid.SelectedRow[1]; //to get the second column value
//string currentColor = colorGrid.SelectedRow[1];
//MessageBox.Show(currentNumber + currentColor);
}
2. For bonus points, why do I have an extra row at the end?
I've found TONS of solutions for this, that are all basically the same. But that doesn't solve my problem.
Thanks in advance.
Edit 1
Since I want this even to happen every time a box is selected, I'm trying something like this inside of myWindow()
this.numberGrid.SelectedCellsChanged += new SelectedCellsChangedEventHandler(numberGrid_SelectionChanged);
and adding a function that hopefully someone can point me in the right direction to fixing.
private void numberGrid_SelectionChanged(object sender, SelectedCellsChangedEventArgs e)
{
MessageBox.Show(numberGrid.SelectedItem.ToString());
}
Try Binding like this,
<DataGridCheckBoxColumn Binding="{Binding Path=IsSelected, Mode=TwoWay}" />
Now you have a Property IsSelected add this to your collection which binds your DataGrid. When you check your CheckBox IsSelected property Set will call and set it to True, now in your collection has the IsSelected True for which its checked.

DataContext to DataGrid in user control

I have a main window with tabcontrol.
Adding a new tabitem with a user control content.
In the xaml:
<Grid><DataGrid DataContext="{Binding Path=Patients, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="Id"
Width="Auto"
Binding="{Binding Id}"/>
In code behind:
Opening context,
var query = from pp in context.Patients select pp;
var Patients = query.ToList();
TabItem patientsView = new TabItem(); // adding new tabitem
StackPanel header = new StackPanel
header.Children.Add(new TextBlock {Text="Patients"});
patientsView.Header = header;
patientsView.Content = new ViewDataPatients{DataContext = Patients};
It refuses to populate the bind data to the grid.
Any idea where I am doing this wrong?
can't understand why you need such approach, but still. You've set DataContext of your UserControl to Patiense, so DataGrid alreay have this data context, enought the following:
<DataGrid DataContext="{Binding}">
But this is redundant in your case. To populate DataGrid with data just set ItemsSource:
<DataGrid ItemsSource="{Binding}">
Hope this helps.
King regards, Nazar

Datagrid is not enabling me to scroll between it's columns if I manually add the data

I'm creating a C# project where many columns of Combo Box are added programmatically in a datagrid.
Unfortunately when adding many columns, the datagrid is not able to display them all, and the horizontal scroll bar is disabled and unclickable..
Design :
<DataGrid ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" x:Name="dg" Grid.Column="1" Grid.Row="0" AutoGenerateColumns="false" Background="#FFDFF9F9" Height="76" VerticalAlignment="Top" Margin="2,82,0,0" HorizontalAlignment="Stretch"/>
Code :
foreach (var r in importMappings)
{
var dgtc = new DataGridTextColumn();
dgtc.Binding = new Binding(string.Format("[{0}]", r.Key));
var sp = new StackPanel();
dgtc.Header = sp;
sp.Children.Add(new Label { Content = r.Key });
var combo = new ComboBox();
sp.Children.Add(combo);
combo.ItemsSource = excelHeaders;
int x = combo.SelectedIndex;
var selectedBinding = new Binding(string.Format("[{0}]", r.Key));
selectedBinding.Source = importMappings;
combo.SetBinding(Selector.SelectedIndexProperty, selectedBinding);
dgtc.CanUserSort = false;
dgtc.CanUserReorder = false;
dg.Columns.Add(dgtc);
}
Live View :
The scroll bar is always disabled here.
Any ideas on how to make the scroll bar work ?
It looks like you don't have any data row. After adding a row of data the scrollbar worked for me.
For testing just edit your datagrid to:
<DataGrid ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" x:Name="dg" Grid.Column="1" Grid.Row="0" AutoGenerateColumns="false" Background="#FFDFF9F9" Height="76" VerticalAlignment="Top" Margin="2,82,0,0" HorizontalAlignment="Stretch">
<TextBlock />
</DataGrid>
The scrollbar should be working now.

Binding ComboBox ItemsSource in DataGrid RowDetailsTemplate

I am trying to bind an ItemsSource to a ComboBox in a RowDetailsTemplate. If I place a ComboBox outside the grid it works fine. I think this is occureing because of the ItemsSource property on the grid may be throwing off the ComboBox within the RowDetailsTemplate. XAML is below any thoughts?
Categories and CatTypes are two different ObservableCollections.
No error is occurring; the ComboBox just appears empty.
<ComboBox ItemsSource="{Binding CatTypes}"></ComboBox>
<my:DataGrid Name="gridProds" AutoGenerateColumns="False"
AlternatingRowBackground="Gainsboro" ItemsSource="{Binding Categories}">
<my:DataGrid.Columns>
<my:DataGridTextColumn x:Name="CatId" Header="CatID" Width="Auto" Binding="{Binding CategoryID}" />
<my:DataGridTextColumn Header="CatName" Width="Auto" Binding="{Binding CategoryName}" />
</my:DataGrid.Columns>
<my:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label>ID:</Label>
<TextBox Name="txtGridCatId" Text="{Binding CategoryID}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Category Type:</Label>
<ComboBox ItemsSource="{Binding CatTypes}"></ComboBox>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</my:DataGrid.RowDetailsTemplate>
</my:DataGrid>
There is a class in the called DataSource in which the following is done:
private ObservableCollection<string> _cattypes = new ObservableCollection<string> { };
public ObservableCollection<string> CatTypes
{
get
{
_cattypes = new ObservableCollection<string> { };
SqlConnection con = new SqlConnection("MyConnStringHere;");
SqlCommand cmd = new SqlCommand("Select ID, CatType from PfCategoryType ORDER BY CatType", con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
string CatType = (string)rdr["CatType"];
_cattypes.Add(CatType);
}
con.Close();
return _cattypes;
}
}
In the MainWindow.xaml.cs I have:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataSource dataSource = new DataSource();
this.DataContext = dataSource;
}
}
If you checked the debug output in VS you would see the actual binding error. Most likely below code will fix it for you.
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=CatTypes}" />
If you can't get RelativeSource to work then use names. The property CatTypes is a property of some class which you created an object for and set as datacontext to some control. Just give that control a name (for example myControl) and bind like this:
<ComboBox ItemsSource="{Binding ElementName=myControl, Path=CatTypes}" />
If that don't work you need to post more of your code to figure out what you are doing wrong.
What happens if you try this?
<ComboBox DataContext="{Binding DataContext, ElementName=myControl}" ItemsSource="{Binding CatTypes}" />
(Of course you'd rename "myControl" to match the name of your window.)
Here, we're setting the data context of the combo box to be the same as the data context of the window. Since this is also the same data context of the first combo box in your XAML, I imagine the second combo box will start behaving like the first. (Although I worry that this will result in some unnecessary database connections, one per grid row.)
On second thought, if you need to set other properties in the context of the row, you won't want to set the data context of the entire ComboBox. In that case, I'd try something like this.
<ComboBox ItemsSource="{Binding ElementName=myControl, Path=DataContext.CatTypes}" SelectedItem="{Binding CategoryType}" />

Categories

Resources