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
Related
I'm experimenting with code-behind created WPF masks as prototype for a WPF mask designer.
In my ViewModel i have a DataTable and a DataView (which is simply the DefaultView of the DataTable).
In my DataTable i got two columns ("vorname" and "nachname") and four rows.
In my WPF mask i want to have a DataGrid and two TextBoxes, which are binded to the SelectedItem of the DataGrid and the columns (either "vorname" or "nachname").
When i select an item in the DatGrid at runtime, the data from that item shall be showed in the TextBoxes.
First i tried to define the DataGrid in the XAML file and generate the TextBoxes an their bindings in code.
Here it works fine.
I select an item in the DataGrid and the data of the item is showed in the TextBoxes.
But when i generate the grid in code, it doesn't work anymore.
Is there some sort of NotifyOnSelectedIndexChanged, that i'm missing?
Any help will be appreciated.
This is the XAML:
<Window x:Class="DesignerTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow"
Height="400"
Width="600">
<DockPanel x:Name="mainpanel">
<!--<DataGrid x:Name="datagrid"
DockPanel.Dock="Top"
Height="120" />-->
<WrapPanel x:Name="wrappanel">
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.vorname, ElementName=datagrid}" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.nachname, ElementName=datagrid}" IsEnabled="False" />-->
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" IsEnabled="False" />-->
</WrapPanel>
</DockPanel>
</Window>
And this is the code for creating and binding:
// The ViewModel und the DataTable are created.
_vm = new SerializingTestViewModel();
_vm.CreateDataTable();
this.DataContext = _vm.DataTable;
// The DataGrid and it's Binding are created.
DataGrid datagrid = new DataGrid();
datagrid.Name = "datagrid";
DockPanel.SetDock(datagrid, Dock.Top);
datagrid.Height = 120;
datagrid.ItemsSource = _vm.DataSource;
mainpanel.Children.Add(datagrid);
// The Textboxes and the Bindings are created.
TextBox vornameSelected = new TextBox();
vornameSelected.Name = "vornameSelected";
vornameSelected.Width = 150;
Thickness margin = new Thickness(5);
vornameSelected.SetValue(TextBox.MarginProperty, margin);
vornameSelected.IsEnabled = false;
Binding selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.vorname");
vornameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(vornameSelected);
TextBox nachnameSelected = new TextBox();
nachnameSelected.Name = "nachnameSelected";
nachnameSelected.Width = 150;
margin = new Thickness(5);
nachnameSelected.SetValue(TextBox.MarginProperty, margin);
nachnameSelected.IsEnabled = false;
selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.nachname");
nachnameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(nachnameSelected);
Try setting your binding source using the Source property instead of ElementName
//selectedItemBinding.ElementName = "datagrid"
selectedItemBinding.Source = datagrid;
The problem might be that the ElementName lookup for items is not working as expected because items are added dynamically at runtime via code behind.
I want to add a button on every row of WPF grid which I am binding from code behind. I am very new to WPF any help is appreciated.
My current code for binding grid is:
DataGridTextColumn c1 = new DataGridTextColumn();
c1.Header = "Dummy column";
c1.Binding = new Binding("DummyColumn");
c1.IsReadOnly = true;
grdDummy.Columns.Add(c1);
foreach (DummyObject deal in AllDummyObjects)
{
ModelToBind dataModel = new ModelToBind()
//do some processing on dataModel
grdDummy.Items.Add(dataModel);
}
You can add another column with button like this:
DataGridTemplateColumn buttonColumn = new DataGridTemplateColumn();
DataTemplate buttonTemplate = new DataTemplate();
FrameworkElementFactory buttonFactory = new FrameworkElementFactory(typeof (Button));
buttonTemplate.VisualTree = buttonFactory;
//add handler or you can add binding to command if you want to handle click
buttonFactory.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(HandleClick));
buttonFactory.SetValue(ContentProperty, "Button");
buttonColumn.CellTemplate = buttonTemplate;
grdDummy.Columns.Add(buttonColumn);
Previously, an example was given of creating a Button using FrameworkElementFactory.
This class is not recommended.
Quote from the documentation:
This class is a deprecated way to programmatically create templates, which are subclasses of FrameworkTemplate such as ControlTemplate or DataTemplate; not all of the template functionality is available when you create a template using this class. The recommended way to programmatically create a template is to load XAML from a string or a memory stream using the Load method of the XamlReader class.
In this regard, I show the implementation code using the XamlReader.
The code is shown from the assumption that the ModelToBind class has a command-property named ButtonCommand and property ButtonTitle.
And this class is located in the local namespace "Febr20y" in the assembly of the same name.
DataGridTextColumn c1 = new DataGridTextColumn
{
Header = "Dummy column",
Binding = new Binding("DummyColumn"),
IsReadOnly = true
};
DataTemplate template = (DataTemplate)XamlReader.Parse(
#"<DataTemplate
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Febr20y;assembly=Febr20y'
DataType ='{x:Type local:ModelToBind}'>
<Button Content='{Binding ButtonTitle, Mode=OneWay}'
Command='{Binding ButtonCommand, Mode=OneWay}'/>
</DataTemplate>");
DataGridTemplateColumn c2 = new DataGridTemplateColumn()
{
Header = "Buttons",
IsReadOnly = true,
CellTemplate=template
};
grdDummy.Columns.Add(c1);
grdDummy.Columns.Add(c2);
var listSource = AllDummyObjects
.Select(deal => new ModelToBind() { ButtonTitle = deal.Title.ToString()})
.ToList();
grdDummy.ItemsSource = listSource;
This is equivalent to this XAML code:
<DataGrid x:Name="grdDummy" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding DummyColumn}"
IsReadOnly="True"
Header="Dummy column"/>
<DataGridTemplateColumn Header="Buttons"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:ModelToBind}">
<Button Content="{Binding ButtonTitle, Mode=OneWay}"
Command="{Binding ButtonCommand, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I have a Datagrid binded to an ObservabelCollection. The datagrid refresh very well when the changes are Add, remove ítems on the collection, but if i change a property of one Ítem on that collection, the grid doesn't refresh.
Grid definition:
<DataGrid x:Name="DatGridPlanillas" ItemsSource="{Binding ListaPlanillas,Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="{x:Static resources:Labels.GENERAL_IdDocumeto}" Binding="{Binding StrIdDocumento,Mode=TwoWay}" ClipboardContentBinding="{x:Null}"/>
<DataGridTextColumn Header="{x:Static resources:Labels.GENERAL_FechaCreacion}" Binding="{Binding DatFechaDocumento}" ClipboardContentBinding="{x:Null}"/>
</DataGrid.Columns>
</DataGrid>
Changes
DocumentsBO MiGestorDeDocumentos = new DocumentsBO(db);
foreach (MyEntity Doc in ListaPlanillas)
{
Documento DocumentoFinal = MiGestorDeDocumentos.NewDocIdByModule(_IntIdModulo);
Doc.StrIdDocumento = DocumentoFinal.StrIdDocumento;
Doc.IntIdDocumento = DocumentoFinal.IntIdDocumento;
Doc.PlanillaAcopioGenerada = true;
Doc.NumDocumentonumero = DocumentoFinal.NumDocumento;
db.entry(Doc).State = EntityState.Modified;
}
The only way I found is to empty original Collection and then restore it
db.SaveChanges();
ObservableCollection<MyEntity> _tmp = _ListaPlanillas;
ListaPlanillas = new ObservableCollection<MyEntity>();
ListaPlanillas = _tmp;
But this sounds to me very ugly way to perform something so simple. How can i do to force the grid to update when just a property of collection was changed?
You need to implement the INotifyPropertyChanged interface on the object that is being placed into the collection. Then fire PropertyChanged events when the object properties are updated. See http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx for an example.
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}" />
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!