Datagrid ComboBox binding two values - c#

I have a combobox which is populated from a list of names which were obtained from selecting from an Observable collection. However, associated with those names is an ID also in that Observable collection. The goal is when the user selects a new name (Say changes "John" to "Jill") I will be able to obtain the ID, not just the name. The only way I can think of doing this is storing the ID also in the combobox somehow. But I don't know how to do that with binding.
<DataGridTemplateColumn Header="Name ">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="namescombo" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Names}"
SelectedItem="{Binding Name, UpdateSourceTrigger=PropertyChanged}" FontSize="12" Background="White" FontFamily="Cambria" BorderBrush="White" BorderThickness="0"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
C#
ObservableCollection<Name> Names = new ObservableCollection<Name>();
Name twofields = new Name();
var NamesQuery =
from p in dataEntities.Names
select new { p.Name, p.Id };
foreach (var p in NamesQuery)
{
Names.Add(new Name
{
ID = p.Id,
Name = p.Name
});
}
Names = Names.Select(p => p.Name).Distinct().ToList();

A ComboBox contains properties for both the DisplayMemberPath and the SelectedValuePath, so you could use it to tell the ComboBox to identify items by the "Id" property, but display the "Name" property to the user.
For example,
<DataGridTemplateColumn Header="Name ">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Names}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedId}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I would recommend using SelectedValue over SelectedItem because WPF compares SelectedItem by .Equals() which defaults to comparing items by reference, and if your SelectedItem is not the exact same reference as the item in your ItemsSource, it won't get selected.
For example, SelectedItem = new Person(1, "Test"); would probably not set the selected item correctly, while SelectedItem = ItemsSource[0] would since it refers to an item that exists in the ItemsSource.
Also, it frequently makes more sense to store just the Id of the selected item on a row instead of the entire object :)

You can bind directly to Name object collection and set DisplayMemberPath to Name property so that strings are shown on GUI but in essence you have complete object binded to comboBox.
This way you can bind SelectedItem to Name object and can access Id and Name property.
<ComboBox ItemsSource="{Binding Names}" // Collection of name objects.
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedNameObject}"/> // Object of type Name.

Related

WPF binding 2 properties from 1 List

So what i am trying to accomplish is that i am trying to bind 2 properties from 1 list to 2 different ComboBoxes.
code:
combobox1.DataContext = class.repository;
combobox2.DataContext = class.repository;
and in xaml
<ComboBox x:Name="combobox1" ItemsSource="{Binding Name}"/>
<ComboBox x:Name="combobox2" ItemsSource="{Binding Password}"/>
example - repository[0] = "NAME1"
The result i get is when i open ComboBox looks like:
1 item - N
2 item - A
3 item - M
and so on..
and result i want is
1 item = NAME1
2 item = NAME2
...
Thanks for replies.
If repository is a string[], you should bind the ItemsSource to the DataContext itself:
<ComboBox x:Name="combobox1" ItemsSource="{Binding}"/>
If repository is an IEnumerable<YourClass> where YourClass is a type with a Name and a Password property, you should also set the DisplayMemberPath property:
<ComboBox x:Name="combobox1" ItemsSource="{Binding}" DisplayMemberPath="Name" />
<ComboBox x:Name="combobox2" ItemsSource="{Binding}" DisplayMemberPath="Password"/>
You should use DisplayMemberPath property of the ComboBox to specify you want to see the value of propery "Name".

Data template parameter

There is list with columns to display. I am using ListView with data templates for cells. And my problem is to access both: row and column from template.
Below is demo, xaml:
<ListView x:Name="listView" ItemsSource="{Binding Items}">
<ListView.Resources>
<GridViewColumn x:Key="Column" x:Shared="False">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding WhatHere}" /> <!-- problem here -->
<Run Text="{Binding Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</ListView.Resources>
<ListView.View>
<GridView />
</ListView.View>
</ListView>
and the code:
public partial class MainWindow : Window
{
public List<string> Items { get; } = new List<string> { "1", "2", "3" };
public MainWindow()
{
InitializeComponent();
DataContext = this;
var a = (GridViewColumn)listView.FindResource("Column");
a.Header = "a";
((GridView)listView.View).Columns.Add(a);
var b = (GridViewColumn)listView.FindResource("Column");
b.Header = "b";
((GridView)listView.View).Columns.Add(b);
}
}
will produce
My aim is to have:
a b
a1 b1
a2 b2
a3 b3
Possible? How do I pass column to DataTemplate ? In fact I want to simply know to which column current cell belongs, but please consider following:
This is simplified case.
In reality data templates are more complicated: many elements with bindings, triggers, etc.
In reality column related data are more complicated than just header.
Columns will be generated at runtime with different headers, etc.
Adding columns and setting Header in code-behind is not a problem, DataTemplate is.
I was thinking to use attached property on GridViewColumn, however, it's not parent of cells in the visual tree:
If you want to pass parameter to data template, then you need more MVVM (c) unknown wpf programmer
#mm8, was right, my design lack one more abstraction to hold column information (column name). I've to create column view model (simplified again):
public class ViewModelColumn
{
public string Column { get; }
public ViewModelColumn(string column)
{
Column = column;
}
}
and the code to add column will become something like
var a = new FrameworkElementFactory(typeof(ContentControl));
a.SetValue(ContentControl.ContentProperty, new ViewModelColumn("a"));
((GridView)listView.View).Columns.Add(new GridViewColumn
{
Header = "a",
CellTemplate = new DataTemplate { VisualTree = a }
});
Cell data template is created in code behind. The idea is to supply ContentControl for all cells with column instance bound to Content, then the view needs another data template (this time fully defined in xaml), to know how to visualize it:
<DataTemplate DataType="{x:Type local:ViewModelColumn}">
<TextBlock>
<Run Text="{Binding Column, Mode=OneTime}" />
<Run Text="{Binding DataContext,RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
Such cell DataContext contains column information. To access row (access item from Items), we have to use ListViewItem.DataContext.
Now the view will looks like this:
I am pretty happy about this solution, mostly about combination of things what makes it working, but I guess it could be improved. Hopefully after posting the answer the question become clearer.

How to provide the ID to the Textbox that is inside the Datagrid's datatemplate in wpf in mvvm model, So that I can differentiate the textbox

In My WPF Application I am using MVVM Model. Datagrid Contains Textbox and Label, when provide the input at the run time in the Textbox, dynamically a description will show in label as per the input in the same row.
But the problem is when I provided the input to a textbox, all the textbox with in the datagrid reflect the same input value as their id is not different in grid. how can I solve this problem.
<Grid>
<DataGrid Name="c1DataGrid1" ItemsSource="{Binding CreditInfo}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Credit" Binding="{Binding Path=Credit}"/>
<DataGridTemplateColumn Header="Percentage">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBox Text="{Binding Path=DataContext.CreditPercentage, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<b:Interaction.Triggers>
<b:EventTrigger EventName="LostFocus">
<b:InvokeCommandAction Command="{Binding Path= DataContext.LostFocusCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding}">
</b:InvokeCommandAction>
</b:EventTrigger>
</b:Interaction.Triggers>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Description">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Width="440" Text="{Binding PercentageDescription}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
If I've understood your code correctly you have a grid with 3 columns. The first column contains some value the second column contains textbox where you can insert a value and the third column contains a textbox that calculates percentage of the first column value taking second column value as the percent.
i.e. you have Credit=50 you type 10 into the second column's textbox and you want 5 to appear in the third column.
If that's correct then there is an easier way to achieve what you want.
You create two new properties in the view model for the items bound to your grid. The first property will contain whatever is entered into the textbox of the second column:
private int _creditPercentage;
public int CreditPercentage
{
get { return _creditPercentage; }
set
{
if (value == _creditPercentage)
return;
_creditPercentage= value;
OnPropertyChanged("CreditPercentage");
OnPropertyChanged("PercentageDescription");
}
}
The second property is going to contain the result of the calculation:
public String PercentageDescription
{
get { return Convert.ToString(Math.Round((double)Credit*Percentage/100), CultureInfo.InvariantCulture); }
}
Now you bind the Percentage property to your TextBox in the second column. And PercentageDescription to your third column:
<DataGridTemplateColumn Header="Percentage">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding CreditPercentage}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Description" Binding="{Binding Path=PercentageDescription}"/>
You might also want to implement some input validation in that textbox in the second column to insure that user can only enter digits.
its worked for me by applying the OnPropertyChanged("CreditPercentage"); in the creditinfo property, also define the percentageDescription property in creditmodel.
public ObservableCollection<Credits> CreditInfo
{
get
{
return infos;
}
set
{
infos = value;
OnPropertyChanged("CreditInfo");
OnPropertyChanged("CreditPercentage");
//OnPropertyChanged("PercentageDescription");
}
}public string PercentageDescription
{
get
{
return percentageDescription;
}
set
{
percentageDescription = value;
OnPropertyChanged("PercentageDescription");
}
}

how to retrieve All Checked Items from radTreeListView SelectedItems Collection

how do i retrieve all Checked Items from a radTreeListView SelectedItems Collection in c#?
The example below from Retrieve All Checked Items isn't working at all.
foreach ( object checkedItem in radTreeView.CheckedItems )
{
// Get the container(RadTreeViewItem) of the checked item
RadTreeViewItem container = radTreeView.ContainerFromItemRecursive( checkedItem );
// Add your logic for handling the checked item scenario here
}
The XML for the RadTreeListView is
<telerik:RadTreeListView x:Name="radTreeListView" ItemsSource="{Binding Items}"
AutoGenerateColumns="False" Grid.RowSpan="2" SelectionChanged="radTreeListView_SelectionChanged" SelectionMode="Extended" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" SelectionChanging="radTreeListView_SelectionChanging" BeginningEdit="radTreeListView_BeginningEdit">
<telerik:RadTreeListView.ChildTableDefinitions>
<telerik:TreeListViewTableDefinition ItemsSource="{Binding Items}" />
</telerik:RadTreeListView.ChildTableDefinitions>
<telerik:RadTreeListView.Columns>
<telerik:GridViewSelectColumn/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name}" Header="Name" />
</telerik:RadTreeListView.Columns>
</telerik:RadTreeListView>
SelectedItems is the key!
I prepared SampleData as described here and capture checked items with following code.
System.Collections.ObjectModel.ObservableCollection<object> selected_items = radTreeListView.SelectedItems;
foreach (object item in selected_items)
{
WarehouseItem warehouseitem = (WarehouseItem)item;
MessageBox.Show(warehouseitem.Name);
}

Getting Text from ComboBox in WPF

I have a ComboBox in WPF and I cant access its selected item text.
I have tried
cbItem.Text;
cbItem.SelectedItem.ToString();
XAML:
<ComboBox Name="cbItem" SelectedValuePath="ITEM_ID">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ITEM_NAME}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Do ITEM_ID and ITEM_NAME come from an object?
String textComboBox = ((ITEMCLASS)cbItem.SelectedItem).ITEM_NAME.ToString();
Try
cbItem.SelectedValue.ToString()
This will work only if combobox values are same as the combobox text
EDIT:
Solution 1
You have to get access to the ComboBox's TextBox:
var str = (TextBox)cbItem.Template.FindName("PART_EditableTextBox", cbItem);
Then you can access the SelectedText property of that TextBox:
var selectedText = str.SelectedText; // This will give you text of selected item
Solution 2
ComboBoxItem typeItem = (ComboBoxItem)cbItem.SelectedItem;
string value = typeItem.Content.ToString();// This will give you text of selected item
Try this
<ComboBox Name="cbItem" SelectedValuePath="ITEM_ID">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Name="txtblck" Text="{Binding ITEM_NAME}" />
</DataTemplate>
</ComboBox.ItemTemplate>
TextBox str = (TextBox)cbItem.FindName("txtblck");
string text = str.Text;

Categories

Resources