Set DisplayMemberPath and SelectedValue in WPF with Code - wpf

I have a DataTable that is coming from a Web Service, which I need to bind to a ComboBox. I have not grokked doing binding in XAML yet so this question is about binding in code instead. So far I have tried
cboManager.DataContext = Slurp.DistrictManagerSelect().DefaultView;
cboManager.DisplayMemberPath = "Name";
cboManager.SelectedValuePath = "NameListId";
cboManager.SetBinding(ComboBox.ItemsSourceProperty, new Binding());
And I have tried
DataTable tbl = new DataTable();
tbl = Slurp.DistrictManagerSelect();
cboManager.ItemsSource = ((IListSource)tbl).GetList();
cboManager.DisplayMemberPath = "[Name]";
cboManager.SelectedValuePath = "[NameListId]";
DataContext = this;
In both cases I get the list of managers to show but when I select from the ComboBox I get [Name] and [NameListId] and not the values I am expecting. What am I doing wrong (other than not using XAML's DataBinding)?
Edit added after answers to my original post came in.
So (based on Rachel's response) try number three looks like this:
using (DataTable tbl = Slurp.DistrictManagerSelect())
{
List<ManagerList> list = new List<ManagerList>();
foreach (var row in tbl.Rows)
{
list.Add(new ManagerList
{
NameListId = (int)row[0],
Name = row[1].ToString()
});
}
}
Assuming I am doing what she meant the correct way I am no getting this error Cannot apply indexing with [] to an expression of type 'object'

Have you tried to just bind to the DataTable directly? Do you have columns Name and NameListId? Leave off the DataContext (you already assigned the ItemsSource).
DataTable tbl = Slurp.DistrictManagerSelect();
cboManager.ItemsSource = tbl;
cboManager.DisplayMemberPath = "Name";
cboManager.SelectedValuePath = "NameListId";
When you cast to the IListSource I suspect it is combining all the columns. If you want to bind to a list then you need to create items that have properties Name and NameListID.

I think that's because your ItemsSource is a DataTable, so each ComboBoxItem contains a DataContext of a DataRow, and the DataRow class doesn't have properties called Name or NameListId
Basically, you're trying to tell the ComboBox to display DataRow.Name, and set the value to DataRow.NameListId, both of which are not valid properties.
I usually prefer to parse data into objects, and bind the ItemsSource a List<MyObject> or ObservableCollection<MyObject>
foreach(DataRow row in tbl.Rows)
list.Add(new MyObject { Name = row[0].ToString(), NameListId = (int)row[1] });
cboManager.ItemsSource = list;

Related

How to: wpf datagrid totally programmatical, including binding to observablecollection

I have to perform the entire thing in code, and am at loss after spending two says in StackOverflow and dear uncle google.
I have a class TestData:InotifyPropertyChanged which includes a string "Name", another string "Specifics" and an bool array "checks". This class is used to build a static Observablecollection "testData" (lowercase).
I'd like to create by code a datagrid, not let it make automatic columns, and present these columns:
DataGridTextColumn with "Name", non-editable.
DataGridTextColumn with "Specifics", editable (and presenting a default value "-").
Three DataGridCheckBoxColums with the "checks" data.
The strucutre is set-up properly, and displays the required headers and the placeholders for the data. There's a button which adds new records to "testData", and additional rows are indeed added to the datagrid, but sadly these are empty.
I need this to be two-way, obviously.
I can't manage to display the values I have set in the declaration of the "testData" for "specifics" and "checks".
In addition, I keep receiving an error saying that "two way binding requires Path or x:path". It's frustrating...
Here's what I tried:
<DataGrid x:Name="dgMainDataGrid" CanUserAddRows="False" AutoGenerateColumns="False" />
and
dgMainDataGrid.ItemsSource = testData;
DataGridTextColumn col1 = new DataGridTextColumn();
col1.Header = "col1Header";
col1.IsReadOnly = true;
col1.Binding = new Binding("Name");
DataGridTextColumn col2 = new DataGridTextColumn();
col2.Header = "col2Header";
col2.IsReadOnly = false;
col2.Binding = new Binding("Specifics");
DataGridCheckBoxColumn col3 = new DataGridCheckBoxColumn();
col3.Header = "col3Header";
col3.IsReadOnly = false;
col3.Binding = new Binding("checks[0]");
DataGridCheckBoxColumn col4 = new DataGridCheckBoxColumn();
col4.Header = "col3Header";
col4.IsReadOnly = false;
col4.Binding = new Binding("checks[1]");
DataGridCheckBoxColumn col5 = new DataGridCheckBoxColumn();
col5.Header = "col3Header";
col5.IsReadOnly = false;
col5.Binding = new Binding("checks[2]");
and class
public class TestData : INotifyPropertyChanged
{
public string Name;
public string Specifics;
public bool[] checks;
}
Thanks, guys.
After #Clemens asked how my class looks, I saw these were not properties. Just sprinkle some {get; set;} over the fields.

problems setting XtraGrid with in-place editor-s (using GridLookUpEdit)

I need an XtraGrid control that has GridLookupEdit to enter column values.
What I managed to do so far:
1) I have configured XtraGrid control
columns: ID, Name, Number
in-place GridLookUp Editors for Name and Number columns
2) on the form Load event I load data from database and set XtraGrid datasource and both repositoryItem(..) datasource, valuemember and displaymember
a) Data are loaded in XtraGrid, I can activate cell but can not choose values from dropdown (from repositoryItem(..)) or enter values manually (i can activate cell in "*" new row but it has the same problem - can not choose from the dropdown or enter values manually) => why?
b) If I don't set datasource for XtraGrid, then both dropdown's are active and i can select values using GridLookupEdit, but when i exit the cell, the cell gets cleared => i believe this is because in this case no xtraGrid.DataSource is set?
var model = (from TableA a in _dbE.TableA select new {ID, Name, Number}).ToList();
//if i comment this line out, then i can choose values from GridLookUpEdit
gridControl1.DataSource = model;
repositoryItemNosaukums.DataSource = model;
repositoryItemNosaukums.ValueMember = "ID";
repositoryItemNosaukums.DisplayMember = "Name";
repositoryItemPieteikumaNr.DataSource = model;
repositoryItemPieteikumaNr.ValueMember = "ID";
repositoryItemPieteikumaNr.DisplayMember = "Number";
What am I missing?
The problem was, that my data source "model" was a List, but it should have been BindingList with properties AllowNew==True and AllowEdit==True.
After DevExpress support asked if the data source is read only, i found this post which finally opened my eyes:
A Problem in DataGridView : datagridview seems readonly to user (WinForms)
correct version of code
//no projections (select new ...) for IBindingList<T>..
//var model = (from TableA a in _dbE.TableA select new {ID, Name, Number}).ToList();
var model = (from TableA a in _dbE.TableA).ToList();
//convert to BindingList
var bindingModel= new BindingList<TableA>(model);
bindingModel.AllowNew=true;
bindingModel.AllowEdit=true;
//bind BindingList to datagrid
gridControl1.DataSource = bindingModel; //model;
//... no changes to repositoryItem(..) stuff
repositoryItemNosaukums.DataSource = model;
repositoryItemNosaukums.ValueMember = "ID";
repositoryItemNosaukums.DisplayMember = "Name";
repositoryItemPieteikumaNr.DataSource = model;
repositoryItemPieteikumaNr.ValueMember = "ID";
repositoryItemPieteikumaNr.DisplayMember = "Number";

Edits to dynamic DataTable not updating in DataGrid

I have a DataTable and when I alter (add,modifiy..ect) it wont reflect on the DataGrid which is bound to the DataTable.
ItemsSource="{Binding TableData,Mode=TwoWay,IsAsync=True}"
Thats the binding ^
Now when I set the RowError
TableData.Rows[x].RowError = ex.Message;
HasError gets set to true... but the DataGrid does not reflect this ( I have a style that marks a row red when there is an error)
Note: My changes are not being reflected on more then setting ErrorMessages, I've also tried adding rows in the ViewModel and those added rows are not relfected either.
About the DataTable:
It has no set columns or anything, it relfects a selected Database table which the user picks.
Binding to a DataTable won't work. You need to first convert your DataTable to an ObservableCollection<>. From here:
public void ConvertDataTable( DataTable dt )
{
DataList = new ObservableCollection<ProjectWorkHours>();
//Scan and arrange data into ObservableCollection
int UserID = 0;
if ( dt.Rows.Count >0 )
{
UserID = int.Parse( dt.Rows[0]["UserID"].ToString() );
//Distill project id list
List<int> ProjectIDList = GetProjectIDList( dt );
for ( int i = 0 ; i < ProjectIDList.Count; i ++ )
{
int ProjectID= ProjectIDList[i];
//Get WorkRecord
int[] MyWorkRecord = GetWorkRecord(dt, ProjectID);
ProjectWorkHours newProjectWorkHours = new ProjectWorkHours(UserID,ProjectID,MyWorkRecord);
DataList.Add( newProjectWorkHours);
}
}
}
The link has a more full example of working with a database and using binding.
What Type is TableData?
If it is a List<> then changing the items in the list will NOT update the binding
If it is an ObservableCollection<> then changing the items in the collection will update the binding

DataSet created in code not Binding to ListView

I have a WPF User Control with a ListView in it that is created based on the DataSet that is passed to it:
public void PopulateList(DataSet ds) {
listView.View = CreateGridViewColumns(ds.Tables[0]);
listData.DataContext = ds.Tables[0];
}
private GridView CreateGridViewColumns(DataTable dt) {
// Create the GridView
var gv = new GridView {AllowsColumnReorder = true};
// Create the GridView Columns
foreach (DataColumn item in dt.Columns) {
var gvc = new GridViewColumn
{
DisplayMemberBinding = new Binding(item.ColumnName),
Header = item.ColumnName,
Width = Double.NaN
};
gv.Columns.Add(gvc);
}
return gv;
}
Now I create the user control in code and call it's PopulateList with the appropriate dataset and this is where the problems are starting:
If I pass in a dataset that was created from a call to the database the list view shows all the data but if i pass in a DataSet that i created in code the ListView shows the Columns but will not show the data
//This is a function that hides the DB call return type is DataSet
var dsPatientSmokingStatusHistory = DataRepository.PatientSmokingStatusProvider.GetHistory(PatientId);
//radGridViewPatientSmokingStatus.DataSource = dsPatientSmokingStatusHistory.Tables[0];
var dt = new DataTable();
string c1 = "Date".PadLeft(23).PadRight(23);
string c2 = "Status".PadLeft(20).PadRight(50);
dt.Columns.Add(c1);
dt.Columns.Add(c2);
int i = 0;
foreach (DataRow row in dsPatientSmokingStatusHistory.Tables[0].Rows) {
var dataRow = dt.NewRow();
dataRow[c1] = ((DateTime)row["Date"]).ToString("MM/dd/yyyy");
dataRow[c2] = row["Status"].ToString();
dt.Rows.Add(dataRow);
dt.Rows[i].AcceptChanges();
i++;
}
DataSet ds = new DataSet();
dt.TableName = "Table";
ds.Tables.Add(dt);
ds.AcceptChanges();
smokingStatusGrid.GridWidth = 455;
smokingStatusGrid.GridHight = 97;
//This line does not show data
smokingStatusGrid.PopulateGrid(ds);
//This line will show data
smokingStatusGrid.PopulateGrid(dsPatientSmokingStatusHistory);
Is there a difference between these two datasets that i don't know about that is preventing me from databinding to it?
Also the user control is being used as an ElementHost in a WinForms application (not sure if this makes a difference)
Your code says:
DisplayMemberBinding = new Binding(item.ColumnName)
This binding constructor takes a string paramter which as per MSDN is "The initial Path for the binding" and is of datatype System.Windows.PropertyPath. I guess, since system tries to find a property with the same name in your class, and your string (item.ColumnName) has spaces at start, it runs into a problem (properties can't start with a space).
Would recommend you to take off the padding that you are doing in column name of your table. Apply any padding/margins in the Header of your GridView.

Programmatically set ComboBox SelectedItem in WPF (3.5sp1)

I have been confused while setting SelectedItem programmaticaly in wpf applications with Net Framework 3.5 sp1 installed. I have carefully read about hundred posts \topics but still confused((
My xaml:
<ComboBox name="cbTheme">
<ComboBoxItem>Sunrise theme</ComboBoxItem>
<ComboBoxItem>Sunset theme</ComboBoxItem>
</ComboBox>
If I add IsSelected="True" property in one of the items - it's dosn't sets this item selected. WHY ?
And i was try different in code and still can't set selected item:
cbTheme.SelectedItem=cbTheme.Items.GetItemAt(1); //dosn't work
cbTheme.Text = "Sunrise theme"; //dosn't work
cbTheme.Text = cbTheme.Items.GetItemAt(1).ToString();//dosn't work
cbTheme.SelectedValue = ...//dosn't work
cbTheme.SelectedValuePath = .. //dosn't work
//and even this dosn't work:
ComboBoxItem selcbi = (ComboBoxItem)cbTheme.Items.GetItemAt(1);//or selcbi = new ComboBoxItem
cbTheme.SelectedItem = selcbi;
The SelectedItem is not readonly property, so why it wan't work?
I think thats should be a Microsoft's problems, not my. Or I have missed something??? I have try playing with ListBox, and all work fine with same code, I can set selections, get selections and so on.... So what can I do with ComboBox ? Maybe some tricks ???
To select any item in the ComboBox and to set it as default item selected just use the below line:
combobox.SelectedIndex = 0; //index should be the index of item which you want to be selected
If i add the combobox and items programmatically, this works for me:
ComboBox newCombo = new ComboBox();
ComboBoxItem newitem = new ComboBoxItem();
newitem.Content = "test 1";
newCombo.Items.Add(newitem);
newitem = new ComboBoxItem();
newitem.Content = "test 2";
newCombo.Items.Add(newitem);
newitem = new ComboBoxItem();
newitem.Content = "test 3";
newCombo.Items.Add(newitem);
newCombo.SelectedItem = ((ComboBoxItem)newCombo.Items[1]);
newCombo.Text = ((ComboBoxItem)newCombo.Items[1]).Content.ToString();
newStack.Children.Add(newCombo);
It also works if it set the ItemSource property programmatically, then set the text to the selected value.
Create a public property in your viewmodel for the theme list and one for the selected item:
private IEnumerable<string> _themeList;
public IEnumerable<string> ThemeList
{
get { return _themeList; }
set
{
_themeList = value;
PropertyChangedEvent.Notify(this, "ThemeList");
}
}
private string _selectedItem;
public string SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
PropertyChangedEvent.Notify(this,"SelectedItem");
}
}
bind your combobox in xaml to the properties like this:
<ComboBox
Name="cbTheme"
ItemsSource="{Binding ThemeList}"
SelectedItem="{Binding SelectedItem}">
</ComboBox>
now all you do is add items to the ThemeList to populate the combobox. To select an item in the list, set the selected property to the text of the item you want selected like this:
var tmpList = new List<string>();
tmpList.Add("Sunrise theme");
tmpList.Add("Sunset theme");
_viewModel.ThemeList = tmpList;
_viewModel.SelectedItem = "Sunset theme";
or try setting the selected item to the string value of the item you want selected in your own code if you want to use the code you currently have - not sure if it will work but you can try.
If you know the index of the item you want to set, in this case it looks like you are trying to set index 1, you simply do:
cbTheme.SelectedIndex = 1;
I found that when you don't know the index, that's when you have the real issue. I know this goes beyond the original question, but for Googlers on that count that want to know how to set the item when the index isn't known but the value you want to display IS known, if you are filling your dropdown with an ItemSource from a DataTable, for example, you can get that index by doing this:
int matchedIndex = 0;
if (dt != null & dt.Rows != null)
{
if (dt.Rows.Count > 0)
{
foreach (DataRow dr in dt.Rows)
{
string myRowValue = dr["SOME_COLUMN"] != null ? dr["SOME_COLUMN"].ToString() : String.Empty;
if (myRowValue == "Value I Want To Set")
break;
else
matchedIndex++;
}
}
}
And then you do simply do cbTheme.SelectedIndex = matchedIndex;.
A similar iteration of ComboBoxItem items instead of DataRow rows could yield a similar result, if the ComboBox was filled how the OP shows, instead.
Is the ComboBox data bound?
If so you are probably better to do it through Binding rather than code ....
See this question ... WPF ListView Programmatically Select Item
Maybe create a new SelectableObject {Text = "Abc Theme", IsCurrentlySelected = True}
Bind a collection of SelectableObjects to the ComboBox.
Essentially setting the IsCurrentlySelected property in the model and having UI update from the Model.
Acording Answer 4
If you already add the Items in the Item source.
Fire the PropertyChangedEvent of the selectet Value.
tmpList.Add("Sunrise theme");
tmpList.Add("Sunset theme");
PropertyChangedEvent.Notify(this,"SelectedItem");

Resources