Binding ComboBox ItemsSource in DataGrid RowDetailsTemplate - c#

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}" />

Related

In wpf Clear ListBox items at Runtime

I am using wpf listbox, i cannot able to clear the list when am calling the reload data function, i just want to reload new data at runtime,while page loading it loads the data correctly, when i refresh the new data is fetched in itemsource i can see that in debug mode, but no new data in listbox, old data remains in the list, i cant even clear, when i call list.items.clear(), i tried lot ways, is there any problem in my XAML binding, the following is my code.
XAML:
<ListBox ItemsSource="{Binding}" HorizontalContentAlignment="Left" x:Name="lstbxindex" Foreground="White" FontSize="20px" Height="400" BorderBrush="#555555" Margin="10,34,16,0" VerticalAlignment="Top" Width="322" Background="#555555" >
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Horizontal" Margin="5" >
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="txtblckroundhour" Height="40px" Width="55px" Text="{Binding RoundedHours}" FontSize="14" Background="#555555" Loaded="txtblckroundhour_Loaded" Foreground="White"></TextBlock>
<Label x:Name="items" MouseDoubleClick="items_MouseDoubleClick" Content="{Binding ProjectRow.Name}" Background="#555555" FontSize="20" Loaded="items_Loaded" Visibility="Visible" Margin="35,0,0,0" Width="230" Foreground="White"></Label>
</StackPanel>
<StackPanel Orientation="Vertical">
<ComboBox Height="40px" Width="290" Margin="-230,0,0,0" Loaded="ComboBox_Loaded" Visibility="Hidden" IsEditable="True" FontSize="20" Background="White" Foreground="Black"></ComboBox>
</StackPanel>
<!--<ComboBox x:Name="ComboBox_AddItem" Height="40px" Width="290" Margin="-35,35,0,0" Loaded="ComboBox_AddItem_Loaded" IsEditable="True" FontSize="20" Background="White" Visibility="Hidden" Foreground="Black"></ComboBox>-->
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Get the list of values
private List<ProjectInformation> projectInformationList1 = new List<ProjectInformation>();
// Here define the actual binding of the userinterface listbox to the in-memory list of objects.
foreach (DtoProjectsRow row in projectsTable.Rows)
{
projectInformationList1.Add(new ProjectInformation(row));
}
lstbxindex.DataContext = projectInformationList1;
In SO I tried some solution but unfortunately it is not work for me. Last I tried,
XAML.cs page
public static readonly DependencyProperty MyListProperty = DependencyProperty.Register("MyList", typeof(ObservableCollection<String>), typeof(Window));
public ObservableCollection<String> MyList
{
get
{
return (ObservableCollection<String>)GetValue(MyListProperty);
}
set
{
SetValue(MyListProperty, value);
}
}
XAML:
<ListBox ItemsSource="{Binding **ElementName=Window**}" HorizontalContentAlignment="Left" x:Name="lstbxindex" Foreground="White" FontSize="20px" Height="400" BorderBrush="#555555" Margin="10,34,16,0" VerticalAlignment="Top" Width="322" Background="#555555" >
Using this above solution listitems are clear but when pageloading the listboxitems are clear but I don't want to clear the iistboxitems, after updating the values from user it will reload the updated value in listbox.
lstbxindex.ItemsSource = null;
But its not work.For pageload listbox loaded all items,every 15 min interval it will call the load function for firsttime it will reload the updatedvalues but second time it will reload the updated values and previous values remains in listbox again.
I misunderstood initially thinking you were using MVVM, instead you're populating the ListView datasource from code behind.
Your line lstbxindex.DataContext = projectInformationList1; does not set the Data as you'd think. Instead try lstbxindex.DataContext = this; which means you're telling your view to look for the data source in code behind.
As such, I suggest adding using System.ComponentModel; and using BindingList, a comparison is here.
private BindingList<ProjectInformation> projectInformationList1 = new BindingList<ProjectInformation>();
And you just need this once:
foreach (DtoProjectsRow row in projectsTable.Rows)
{
projectInformationList1.Add(new ProjectInformation(row));
}
lstbxindex.DataSource = projectInformationList1;
As mentioned in the comments, if you did not use ItemsSource="{Binding projectInformationList1}" as I suggested in a comment to your question, this is the alternative:
private ObservableCollection<ProjectInformation> projectInformationList1 = new ObservableCollection<ProjectInformation>();
foreach (DtoProjectsRow row in projectsTable.Rows)
{
projectInformationList1.Add(new ProjectInformation(row));
}
lstbxindex.DataContext = projectInformationList1;
You should have a view model class with a collection property, e.g. like this:
public class ViewModel
{
public ObservableCollection<ProjectInformation> Projects { get; }
= new ObservableCollection<ProjectInformation>();
}
Set the DataContext of your Window or Page in XAML like this:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
and bind the ListBox like this:
<ListBox ItemsSource="{Binding Projects}">
...
</ListBox>
To clear all items in the source collection, access the DataContext in code behind:
var vm = (ViewModel)DataContext;
vm.Projects.Clear();
Edit: Instead of assigning the DataContext in XAML, you may as well do it in code behind, even before the Page or Window is initialized:
public MainWindow()
{
DataContext = new ViewModel();
InitializeComponent();
}
Added the line in loadfunction, Initially set null for ItemSource and then set null to the list object
lstbx.ItemsSource=null;
lstbx.Items.Clear();
ProjectInfoList1=null;
it will clear the listboxitems and reload with updated values only.
private BindingList<ProjectInfo> projectInfoList1 = new BindingList<ProjectInfo>();
Public void loadfunction()
{
lstbx.ItemsSource=null;
lstbx.Items.Clear();
ProjectInformationList1=null;
foreach (DtoProRow row in table.Rows)
{
projectInfoList1.Add(new ProjectInfo(row));
}
lstbx.DataContext = projectInfoList1;
}

Bind list to listboxitem in wpf

I'm trying to bind a list to a listbox in WPF. But it doesn't seem to work, I just see nothing on screen.
Here is my code:
WPF
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="453" VerticalAlignment="Top" Width="119" Margin="0,43,0,0" ItemsSource="{Binding orderlist}">
<ListBoxItem Content="{Binding orderlist.ID}"></ListBoxItem>
</ListBox>
C#
Order order = new Order();
Klantgegevens klantgegevens = new Klantgegevens();
XmlReader rdr = XmlReader.Create(#"C:\Users\Gebruiker\Desktop\EDI\Rekening.xml");
rdr.ReadToFollowing("datum");
order.DatumOntvangst = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("volgnr");
order.Status = "Aangenomen";
order.Opmerkingen = "";
rdr.ReadToFollowing("naam");
order.Afzender = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("naam");
klantgegevens.Naam = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("straat");
klantgegevens.Straat = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("huisnr");
klantgegevens.Huisnummer = rdr.ReadElementContentAsInt();
rdr.ReadToFollowing("plaats");
klantgegevens.Woonplaats = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("postcode");
klantgegevens.Postcode = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("telefoonnr");
klantgegevens.Telefoonnummer = rdr.ReadElementContentAsString();
order.Klantgegevens = klantgegevens;
orderlist.Add(order);
listBox.DataContext = orderlist;
As you probably know, Order is a custom class, and so is Klantgegevens.
I'm pretty new to binding and WPF in general so excuse me for my stupidness :)
You need to set or bind the ItemsSource property of ListView to an IEnumerable. Since you have set the DataContext property to your "orderlist" you should bind the ItemsSource property directly to the DataContext (ItemsSource="{Binding}"). You should also use an ItemTemplate as suggested by Fruchtzwerg :
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="453" VerticalAlignment="Top" Width="119" Margin="0,43,0,0" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Also note that the DataContext of the ItemTemplate is an item in your ItemsSource, i.e. an Order object in this case. So to bind to the "ID" property of the Order object you use the binding syntax above. "ID" must be a public property of the Order class.
With
<ListBoxItem Content="{Binding orderlist.ID}"></ListBoxItem>
you are adding an item in XAML. But your plan is to create a template to present bound items. The simplest solution is to use
<ListBox x:Name="listBox" DisplayMemberPath="ID"/>
if only one property needs to be presented. Multiple properties can be showed by creating a template like
<ListView x:Name="listBox">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ID}" />
<TextBlock Text="{Binding datum}"/>
<!-- ... -->
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Furthermore you should use a property like
public ObservableCollection<Klantgegevens> Items { get; } =
new ObservableCollection<Klantgegevens>();
to bind on. Set the DataContext of the whole Window with the ListView to the object, with this property. After that you can bind the ListView with
<ListView ItemsSource="{Binding Items}"/>

secondary listbox not show data

i've two listbox binded with properties of my viewmodel, the first listbox shows LstEmpresas and work fine, when I select a item, the property SelectedCompany sets fine, all ok.
In SelectedCompany's set of my viewmodel, I call a method than pupulate a secondary list (LtsEjercicios) and work fine too (LtsEjercicios populate perfectly depends that item i've selected in the first listbox).
The secondary listbox binds his ItemSource from LtsEjercicios object that, across viewmodel, is updated.
But the secondary listbox NOT SHOW any data, i'm crazy yet.
This viewModel code
public class frmEmpresasVM : ViewModelBase
{
//propiedades
EmpresasDataAccess empresasController = new EmpresasDataAccess();
private ObservableCollection<EmpresasTable> lstEmpresas;
private ObservableCollection<EmpresasEjerciciosTable> ltsEjercicios;
public ObservableCollection<EmpresasTable> LstEmpresas
{
get
{
return lstEmpresas;
}
set
{
lstEmpresas = value; RaisePropertyChanged("LstEmpresas");
}
}
public ObservableCollection<EmpresasEjerciciosTable> LtsEjercicios
{
get
{
return ltsEjercicios;
}
set
{
ltsEjercicios = value; RaisePropertyChanged("LtsEjercicios");
}
}
//selected company in listbox
private int selectedCompany;
public int SelectedCompany
{
get
{
return selectedCompany;
}
set
{
selectedCompany = value;
LtsEjercicios = empresasController.SelectExercicesById(selectedCompany.ToString());
RaisePropertyChanged("SelectedCompany");
}
}
//main constructor, default values for lists
public frmEmpresasVM()
{
LstEmpresas = empresasController.SelectOnlyNames();
LtsEjercicios = empresasController.SelectExercicesById("0");
}
and, XAML for the view
<ListBox x:Name="companyList" HorizontalAlignment="Left" Height="205" Margin="20,30,0,0" VerticalAlignment="Top" Width="450" ItemsSource="{Binding LstEmpresas, Mode=OneWay}" SelectedValue="{Binding SelectedCompany, Mode=TwoWay}" SelectedItem="{Binding LtsEjercicios, Mode=OneWay}" SelectedIndex="0" DisplayMemberPath="Nombre" SelectedValuePath="Id" IsSynchronizedWithCurrentItem="True" SelectionChanged="companyList_SelectionChanged_1">
<ListBox.ItemBindingGroup>
<BindingGroup/>
</ListBox.ItemBindingGroup>
<ListBox.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</ListBox.DataContext>
</ListBox>
<TextBlock x:Name="lblEjercicio" HorizontalAlignment="Left" Height="20" Margin="475,10,0,0" TextWrapping="Wrap" Text="Ejercicios" VerticalAlignment="Top" Width="95"/>
<ListBox x:Name="excercicesList" HorizontalAlignment="Left" Height="205" Margin="475,30,0,0" VerticalAlignment="Top" Width="110" ItemsSource="{Binding LtsEjercicios, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Ejercicio" SelectedValuePath="Id" SelectedIndex="0" IsSynchronizedWithCurrentItem="True">
<ListBox.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</ListBox.DataContext>
Al Data provides from a MySQL Database from both tables (empresas y empresas_ejercicios).
My goal is, when user select a item in listbox1 (empresas) show exercices in listbox2 (empresas_ejercicios).
If exist other path to determine this operation, i'm all eyes!!!
Thanks in advance.
Not sure if this is the only problem in your code, but what you're doing by setting
<ListBox.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</ListBox.DataContext>
for both list boxes is creating a separate instance of the ViewModel for each list box. Now I guess if you make any changes in the first list box' ViewModel instance, these changes will not be reflected in the second list box because this one binds to a totally different ViewModel instance.
Instead, try globally binding the ViewModel to the whole window / page / user control / etc. (depending on whether you;re doing WPF, Windows Phone, etc.), and let the list boxes inherit it to ensure that only one ViewModel instance is involved:
<Window.DataContext>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM/>
</Window.DataContext>
EDIT:
Alternatively, you might as well instantiate the ViewModel once and store it as global resource:
<Window.Resources>
<InnovaCommerceHosteleryViewModels:frmEmpresasVM x:Key="Viewmodel" />
</Window.Resources>
and in the listboxes' DataContext just reference this global resouce:
<ListBox DataContext="{StaticResource Viewmodel}" ... />

Editable WPF ComboBox does not fire PropertyChanged

I have a not editable ComboBox to display all tables of and SQL Database.
<ComboBox Grid.Column="1"
Grid.Row="2"
Height="23"
Margin="3,3,3,3" Name="cbLogTable" VerticalAlignment="Top"
ItemsSource="{Binding}"
TextSearch.TextPath="TABLE_NAME"
SelectedValue="{Binding Path=LogTable, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnDataErrors=True}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TABLE_NAME}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The property of the containing UserControl looks like this and also implements the INotifyPropertyChanged:
public string LogTable
{
get
{
return _logTable;
}
set
{
if (_logTable == value) return;
_logTable = value;
OnPropertyChanged("LogTable");
}
}
I use the following data binding to fill the ComboBox:
private void UpdateLogTable()
{
var connection = new SqlConnection(_connectionString);
connection.Open();
DataTable t = connection.GetSchema("Tables");
cbLogTable.DataContext = t;
connection.Close();
}
But I don't receive a PropertyChanged notification on changing the selected value of the ComboBox. Where is my fault?
In the binding of SelectedValue:
SelectedValue="{Binding Path=LogTable,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay,
ValidatesOnDataErrors=True,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}}"
Otherwise, the binding is looking for LogTable property on the DataTable type (which is the DataContext for the Combobox), and fails silently.

How to bind a datatable to a wpf editable combobox: selectedItem showing System.Data.DataRowView

I bound a datatable to a combobox and defined a dataTemplate in the itemTemplate.i can see desired values in the combobox dropdown list,what i see in the selectedItem is System.Data.DataRowView here are my codes:
<ComboBox Margin="128,139,123,0" Name="cmbEmail" Height="23" VerticalAlignment="Top" TabIndex="1" ToolTip="enter the email you signed up with here" IsEditable="True" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=username}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
The code behind is so :
if (con != null)
{
con.Open();
//users table has columns id | username | pass
SQLiteCommand cmd = new SQLiteCommand("select * from users", con);
SQLiteDataAdapter da = new SQLiteDataAdapter(cmd);
userdt = new DataTable("users");
da.Fill(userdt);
cmbEmail.DataContext = userdt;
}
I've been looking for something like SelectedValueTemplate or SelectedItemTemplate to do the same kind of data templating but i found none.
I'll like to ask if i'm doing something wrong or it's a known issue for combobox binding?
if something is wrong in my code please point me to the right direction.
thanks for reading this
By default the combo box will call ToString() on the selected item to get a value to show - since you are binding to a DataRowView the result is the type (the default behaviour of ToString())
What you actually want to show it the username property of the selected item and to do this you can set the DisplayMemberPath of the combobox to "username"
(Also, if you do this, you'll probably find that you can get rid of the custom datatemplate too, since the username will also be used to populate each item.)
In response to your comment:
I don't want to be one of those programmers, but "it works on my machine".
My XMAL is:
<ComboBox Name="cmbEmail"
Height="23"
VerticalAlignment="Top"
TabIndex="1"
ToolTip="enter the email you signed up with here"
IsEditable="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
DisplayMemberPath="username">
</ComboBox>
and my code behind is:
public partial class Window1 : Window
{
public Window1()
{
Users = new DataTable("users");
Users.Columns.Add("username");
Users.Rows.Add(CreateDataRow("Fred"));
Users.Rows.Add(CreateDataRow("Bob"));
Users.Rows.Add(CreateDataRow("Jim"));
InitializeComponent();
cmbEmail.DataContext = Users;
}
public DataTable Users { get; private set; }
private DataRow CreateDataRow(string userName)
{
DataRow dr = Users.NewRow();
dr["username"] = userName;
return dr;
}
}
i got an answer form msdn and it did it for me
<ComboBox IsEditable="True" ItemTemplate="{StaticResource comboTemplate}" ItemsSource="{Binding}" TextSearch.TextPath="username">
the reason was
Since the ComboBox is set to IsEditable="True", and the ComboBox contains a TextBox element to show the selected value. But the item in the ComboBox is RowView type; it shows the type information not the value in the TextBox.
notice the
TestSearch.TextPath="username"
I've found out why it wasn't working.Using DisplayMemberPath wasn't working but rather ItemTemplate.
Here is an example.
<Window.Resources>
<DataTemplate x:Key="comboTemplate">
<TextBlock Text="{Binding Path=username}" />
</DataTemplate>
</Window.Resources>
<ComboBox Margin="18,121,24,0" Name="cmbEmail" Tag="email" TabIndex="1" ToolTip="enter the email you signed up with here" IsEditable="True" IsSynchronizedWithCurrentItem="True" ItemTemplate="{StaticResource comboTemplate}" ItemsSource="{Binding}" Height="23" VerticalAlignment="Top" Style="{DynamicResource cmbBoxerrors}">
<ComboBox.Text>
<Binding Path="username"/>
</ComboBox.Text>
</ComboBox>
Notice the ItemTemplate
the xaml.cs code hasn't change. thanks for those who read and suggested solutions to me.

Categories

Resources