wpf datagridcombobox linq foreign key - wpf

I have searched all over and keep finding similar examples that are not working for me. Can someone point out where I am messing up?
I have two tables AlarmName and AlarmLevel. Linked by a foreign key AlarmName.AlarmLevelID -> AlarmLevel.Id.
I have the following in my XAML:
<DataGrid Name="Alarms" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="75" />
<DataGridTextColumn Binding="{Binding Description}" Header="Description" />
<DataGridTextColumn Binding="{Binding AlarmLevel.Name}" Header="AName" Width="75"/>
<DataGridTemplateColumn Header="Alarm Level">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.AlarmLevel, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
DisplayMemberPath="Name"
SelectedValuePath="AlarmLevelID"
SelectedValue="{Binding Id}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And the following in my code behind:
UISimulation1.Data.UISimulationDBDataContext db = new UISimulation1.Data.UISimulationDBDataContext();
var alarm = (from v in db.AlarmNames
select v);
Alarms.ItemsSource = alarm;
The DataGridTextColumns work and pull the right data. However, I can not make the combobox work or display anything no matter what I try.
Clearly, I am missing something obvious; but have tried 50 variants of the combobox based on google examples with zero results.
--rt

You need to expose the records in the AlarmLevel table using a public property that you bind the ItemsSource property of the ComboBox to. Please refer to the following sample code.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
using (UISimulation1.Data.UISimulationDBDataContext db = new UISimulation1.Data.UISimulationDBDataContext())
{
AlarmNames = (from v in db.AlarmNames
select v).ToList();
AlarmLevels = (from v in db.AlarmLevels
select v).ToList();
}
}
public IEnumerable AlarmNames { get; private set; }
public IEnumerable AlarmLevels { get; private set; }
}
<DataGrid Name="Alarms" ItemsSource="{Binding AlarmNames}" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="75" />
<DataGridTextColumn Binding="{Binding Description}" Header="Description" />
<DataGridTextColumn Binding="{Binding AlarmLevel.Name}" Header="AName" Width="75"/>
<DataGridTemplateColumn Header="Alarm Level">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.AlarmLevels, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
DisplayMemberPath="Name"
SelectedValuePath="AlarmLevelID"
SelectedValue="{Binding Id}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

The DataGrid columns don’t belong to the visual or logical tree of the DataGrid, see this

Related

WPF - datagrid - display a text box to set the value by user when its row was selected

I have a data grid that includes a list of stock items that are displayed as rows. How can I implement in which a text box is displayed for entering the desired order quantity when the user selects the item row? thanks in advance.
XAML
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=ItemId}"></DataGridTextColumn>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" Binding="{Binding Path=Price}"></DataGridTextColumn>
<DataGridTextColumn Header="InStock" Binding="{Binding Path=InStock}"></DataGridTextColumn>
<DataGridTextColumn Header="Quantity" x:Name="QuantityTextBox" Visibility="Hidden"/>
</DataGrid.Columns>
you may use `DataGridTemplateColumn as the following :
<DataGridTemplateColumn Header="{x:Static p:Resources.Quantity}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Quantity,UpdateSourceTrigger=PropertyChanged}" Width="100" PreviewTextInput="CheckIsInteger" TextChanged="QuantityChanged" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Then you may also check that user only inserts numbers (CheckIsInteger) :
private void CheckIsInteger(object sender, TextCompositionEventArgs e)
{
int parsedValue;
if (!int.TryParse(e.Text, out parsedValue))
{
e.Handled = true;
}
}

datagride Mvvm update and insert from external fields

I have data grid that I want to update from external fields
I don't want to directly affect the data base
<DataGrid
AlternatingRowBackground="CadetBlue "
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="True"
ItemsSource="{Binding Path=Addresss ,Mode=TwoWay}"
SelectedItem="{Binding Path=Address, Mode=OneWayToSource}"
ColumnHeaderStyle="{StaticResource CenterColumnHeader}"
CellStyle="{StaticResource DataGridContentCellCentering}">
<DataGrid.Columns>
<DataGridTextColumn
Width="70"
Binding="{Binding Path=AddType}"
Header="النوع"
/>
<DataGridTextColumn
Width="80"
Binding="{Binding Path=AddAdjective}"
Header="الوصف" />
<DataGridTextColumn
Width="250"
Binding="{Binding Path=AddAddress}"
Header="العنوان" />
</DataGrid.Columns>
</DataGrid>
I made a combo box
<ComboBox x:Name="Cbox1" Width="69"
materialDesign:HintAssist.Hint="النوع"
Background="#252525"
ItemsSource="{Binding Path=AddTypelist}"
SelectedValue="{Binding Path=Addressl.AddType}"
Style="{StaticResource MaterialDesignFilledComboBoxFocus}"
IsTextSearchEnabled="True" IsTextSearchCaseSensitive="True"/>
I don't want the change to happen automatically. When I change it in the combo box, I want the process to be done with a button
I tried this method
public AddressTable Address
{
get => _address;
set
{
_address = value;
_addressl = _address;
NotifyOfPropertyChange(()=>Address);
}
}
private AddressTable _addressl;
public AddressTable Addressl
{
get => _addressl;
set
{
_addressl = value;
NotifyOfPropertyChange(() => Addressl);
}
}
But it didn't work

Pass values to ValueConverter from two different binding sources in single DataGrid

What I want to do is to color DataGridRow red if validation fails. I attempted to do so with ValueConverter but I have no idea how to pass value from more than one binding source.
I've got datagrid with binding to collection of objects. Then there's a
DataGridTemplateColumn with ComboBox and Textbox inside (only one is visible at time), and the ComboBox has non related to bound collection binding source.
See code:
<DataGrid Grid.Row="4" Grid.Column="0" x:Name="DGR_Slots" Grid.ColumnSpan="5" Grid.RowSpan="3" ItemsSource="{Binding ElementName=WND_ItemCraftingWindow, Path=SelectedSchematic.Slots}" AutoGenerateColumns="False" ColumnWidth="*">
<DataGrid.Resources>
<classes:ValidationConverter x:Key="ValidationConverter" />
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="{Binding Converter={StaticResource ValidationConverter}}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}" Visibility="Collapsed"/>
<DataGridTextColumn Header="Slot Type" Binding="{Binding SlotType}" />
<DataGridTextColumn Header="Mat Type" Binding="{Binding MaterialType}" />
<DataGridTextColumn Header="Count" Binding="{Binding MaterialCount}" />
<!--<DataGridComboBoxColumn Header="Material" />-->
<DataGridTemplateColumn Header="Material">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Visibility="Visible" MouseDoubleClick="MaterialCell_MouseDoubleClick" ItemsSource="{Binding ElementName=WND_ItemCraftingWindow, Path=Materials}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectionChanged="MaterialComboBox_SelectionChanged"/>
<TextBox Visibility="Collapsed" MouseDoubleClick="MaterialCell_MouseDoubleClick" PreviewKeyUp="MaterialCell_TextBox_PreviewKeyUp" KeyUp="MaterialCell_TextBox_KeyUp" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
WND_ItemCraftingWindow is the Window that owns the datagrid and the Materials is ObservableCollection property inside that window.
I need to pass the object from DataGrid.ItemsSource collection (that works now) and SelectedItem from Combobox in same row (i dont know how to do that) into ValidationConverter.
Validation of the row can be performed only with the object from DataGridRow and SelectedItem from combobox.
Also I'm open to different solutions to color the row after such validation.
Alright I managed to do it hackish way.
Datagrid code (removed meaningless parts):
<DataGrid x:Name="DGR_Slots" ItemsSource="{Binding ElementName=WND_ItemCraftingWindow, Path=SelectedSchematic.Slots}" AutoGenerateColumns="False" ColumnWidth="*" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Material">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Visibility="Visible" ItemsSource="{Binding ElementName=WND_ItemCraftingWindow, Path=Materials}" SelectionChanged="MaterialComboBox_SelectionChanged" />
<TextBox Visibility="Collapsed" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
SelectionChanged event
private void MaterialComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// ...
var slot = (DGR_Slots.SelectedItem as SchematicSlot);
var mat = ((sender as ComboBox).SelectedItem as Material);
var row = WPFHelper.GetRow(DGR_Slots, DGR_Slots.SelectedIndex);
row.Background = new SolidColorBrush(slot.MaterialType != mat.MaterialType ? Colors.Red : Colors.White);
}
WPFHelper GetRow method (found somewhere on the internet)
static public DataGridRow GetRow(DataGrid dg, int index)
{
DataGridRow row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
// may be virtualized, bring into view and try again
dg.ScrollIntoView(dg.Items[index]);
row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
Although I'm still open to other solutions.

Binding to property of datacontext parent- WPF

I have a DataGrid:
<DataGrid x:Name="DG" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="?" Binding="{Binding l}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
In DataContext of the DataGrid there is the collection of class X:
public ObservableCollection<xxx> col{ get; set; }// = DataContext of DG
private string lName;
public string LName
{
get { return lName; }
set
{
lName= value;
NotifyPropertyChanged("LName");
}
}
I want lName will be the header of a particular column in DataGrid
I try this way:
<DataGridTextColumn Binding="{Binding l}">//l=prop of xxx class that contains the collection
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.LName,
RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
It did not work
how can do this?
Try {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.LName}
Also I wouldn't play with name cases. WPF is pretty case-sensivity. Avoid of using one-symbol names
Binding paths are also case-sensitive. There is no property lName in your DC.

Bind linq to DataGridComboBoxColumn

I am using wpftoolkit from wpf.codeplex.com . While binding a data table(through linq object list) there is a collumn that contains a reference of another table content as a foreign key. So, generally, currently, on that column in datagrid, i want to show a combo box containing the element of another table and selelted item should be from my concerned table. Here is what I am writing:
<dg:DataGrid x:Name="UsersGrid" AutoGenerateColumns="False" CellEditEnding="UsersGrid_CellEditEnding" RowEditEnding="UsersGrid_RowEditEnding" PreviewKeyDown="UsersGrid_PreviewKeyDown">
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Binding="{Binding Path=Id}" Header="Id" Visibility="Hidden" />
<dg:DataGridTextColumn Binding="{Binding Path=Username}" Header="User Name" />
<dg:DataGridTextColumn Binding="{Binding Path=Password}" Header="Password" />
<dg:DataGridTextColumn Binding="{Binding Path=RoleId}" Header="Role ID" />
<dg:DataGridComboBoxColumn x:Name="Roles" ItemsSource="{Binding Path=TVRole}">
</dg:DataGridTemplateColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
For making the binding, the code behind file contains as the following code:
UsersGrid.ItemsSource = UserManager.GetAllUsers();
Problem is that, I am now don't know what to place in the "ItemsSource" property to achieve what i want for the DataGridComboBoxColumn column . Can anyone please help me? Again mentioning, i want to bind it with linq object.
There is a strange bug with a DataGridComboBoxColumn: the ItemsSource property doesn't support advanced bindings. But you can use DataGridTemplateColumn, and code will be like this:
<DataGrid ItemsSource="{Binding Users}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Id}" Header="Id" Visibility="Hidden" />
<DataGridTextColumn Binding="{Binding Path=Username}" Header="User Name" />
<DataGridTextColumn Binding="{Binding Path=Password}" Header="Password" />
<DataGridTextColumn Binding="{Binding Path=RoleId}" Header="Role ID" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Roles, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"
DisplayMemberPath="Title" SelectedValuePath="Id" SelectedValue="{Binding RoleId, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And add this code to the constructor of the View:
var model = new MainViewModel() { Users = UserManager.GetAllUsers(), Roles = UserManager.GetAllRoles() };
this.DataContext = model;
Where the MainViewModel is an auxiliary class:
public class MainViewModel
{
public List<Role> Roles { get; set; }
public List<User> Users { get; set; }
}
Another way is using a list of UserViewModel instead of a list of User, where each viewmodel class contains the list of roles, so it will be possible to use a correct binding. But it is a more complicated solution than above-mentioned one.

Resources