WPF Combobox display only SOME items - c#

So I have a WPF app(with MVVM) and in this I have a combobox which binds to a table in my database and displays the values, this works just fine.
However, now I want to make a new combobox and bind it to the same table, but now I only want it to display SOME of the values. Is there a simple way to do this?
The table has has four entries but I only want to show 3 of them in this new combobox.
I know I could just make a new table in the database to bind to, but I might have to use several of these comboboxes(with different values) and I'd rather not go through all that bother if I can avoid it.
XAML:
<ComboBox
Name="cmComp"
MinWidth="150"
Margin="12 0 0 12"
ItemsSource="{Binding SelectedComponentLookup}"
DisplayMemberPath="ComponentChoice"
SelectedValuePath="ComponentChoice"
SelectedItem="{Binding ComponentChosen}">
</ComboBox>
VIEWMODEL:
private IEnumerable<ComponentLookupDto> _selectedComponentLookup;
public IEnumerable<ComponentLookupDto> SelectedComponentLookup
{
get { return _selectedComponentLookup; }
set
{
_selectedComponentLookup = value;
}
}
DTO:
public class ComponentLookupDto
{
public int ComponentLookupId { get; set; }
public string ComponentChoice { get; set; }
}

The way I achieve this is that I filter out the items I don't want to display in the getter for the property to which I bind my ItemsSource. :
XAML:
<ComboBox ItemsSource={Binding SelectedComponentLookupOther} ... />
And in your ViewModel:
public IEnumerable<ComponentLookupDto> SelectedComponentLookupOther
{
get { return _selectedComponentLookup.Where(c => c.SomeProperty == "however you want to pick it out"); }
}

Related

Seperate object per column in a datagrid row

I have a datagrid from SCADA software that I am recreating in WPF by creating a custom user control that can be used for more use cases. I have an observable collection of type SettingRow that contains the data of one cell. The problem is that it is not containing the data of one cell.
If I have 10 items in my collection than I cannot see an seperate item in for example Row 1, Column 3. I see the same item in for example Row 1, Column 2. So in short: I see the same SettingRow object no matter what column I select in a row.
Every cell is an seperate setting object that is bound to an DataTemplateSelector to select the appropiate datatemplate to display the value (boolean as checkbox, string and value as textbox etc.)
I have created a DataGrid that has a templateselector based on the value in the SettingRow class. The datagrid's itemssource is currently bound to the ObservableCollection in my viewmodel. It works, I see every SettingRow in each cell per row, but I am quite sure that I am seeing the same SettingRow in each column which is not an seperate object that is independent of the object in the column before that one.
I've tried some solutions on StackOverflow with an observable collection inside an observable collection but I do not really get it to work.
Underneath is the class that is displayed in each row. Does this object get copied over to the next column? Or is the same object re-used in every column?
public class SettingRow : ObservableObject
{
public int ID { get; set; }
public object Value { get; set; }
public string Name { get; set; }
public string Unit { get; set; }
}
My datagrid in XAML is declared and only has the Name property. The rest are added through the code.
<DataGrid x:Name="OverviewDataGridView" ItemsSource="{Binding SettingsCollection, Mode=TwoWay}" AutoGenerateColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}">
</DataGridTextColumn>
</DataGrid.Columns>
Below I am creating a column per phase. The 4 phases are simulated since I do not have actual data. Now, every SettingRow should be individually editable and I need to retrieve it from the ViewModel.
This is in the code behind.
public void GenerateColumns()
{
//Simulate 4 phases and adding a column per phase!
for(int i = 0; i < 4; i++)
{
DataGridTemplateColumn templateCol = new DataGridTemplateColumn();
templateCol.Header = "Phase " + i;
TemplateSelector templateSel = new TemplateSelector();
templateSel.BooleanTemplate = this.Resources["BooleanTemplate"] as DataTemplate;
templateSel.TextTemplate = this.Resources["TextTemplate"] as DataTemplate;
templateCol.CellTemplateSelector = templateSel;
this.OverviewDataGridView.Columns.Add(templateCol);
}
}
The ViewModel is quite simple it creates some test data. Every SettingRow that is created is displayed in every column, but I think I see the same object in each
public class DynamicDataGridViewModel : ObservableObject
{
private ObservableCollection<SettingRow> settingsCollection;
public ObservableCollection<SettingRow> SettingsCollection
{
get { return settingsCollection; }
set
{
settingsCollection = value;
RaisePropertyChangedEvent("SettingsCollection");
}
}
public DynamicDataGridViewModel()
{
SettingsCollection = new ObservableCollection<SettingRow>();
SettingsCollection.Add(new SettingRow() { ID = 0, Name = "Phase Active", Value = true, Unit = "" });
SettingsCollection.Add(new SettingRow() { ID = 1, Name = "Minimum phase duration", Value = +900, Unit = "s" });
SettingsCollection.Add(new SettingRow() { ID = 2, Name = "Min Supply air temperature", Value = 50.0302, Unit = "C" });
}
}
And lastly, my DataTemplateSelector. It only handles the value of the SettingRow and display the correct symbol (boolean = checkbox etc.)
public class TemplateSelector : DataTemplateSelector
{
public DataTemplate BooleanTemplate { get; set; }
public DataTemplate TextTemplate { get; set; }
public SettingRow Content { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ContentPresenter presenter = container as ContentPresenter;
DataGridCell cell = presenter.Parent as DataGridCell;
SettingRow row = (cell.DataContext as SettingRow);
if(row != null && row.Value != null)
{
this.Content = row;
switch (Type.GetTypeCode(row.Value.GetType()))
{
case TypeCode.Boolean:
return BooleanTemplate;
default:
return TextTemplate;
}
}
else
{
return null;
}
}
}
EDIT:
Templates for the columns below
<DataTemplate x:Key="BooleanTemplate">
<StackPanel Orientation="Horizontal" Margin="2">
<CheckBox IsChecked="{Binding Value}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TextTemplate">
<StackPanel Orientation="Horizontal" Margin="2">
<TextBox Height="25" MinWidth="70" FontSize="14">
<TextBox.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="Value" Mode="TwoWay"/>
<Binding Path="Unit" Mode="TwoWay"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
</DataTemplate>
What I need is a DataGrid bound to an observable collection. Which has dynamic amount of columns (represents an phase) and I need one SettingRow in every column and it to be editable per object. So that I can for example set the visibility of the Phase Duration setting in the Levelling phase to invisible.
EDIT 2:
What I have in mind is to use a observable collection inside an observable collection like the snippet below this way I can represent my data as the columns in each row. This way I can access every row and column individual object.:
private ObservableCollection<ObservableCollection<SettingRow>> items;
public ObservableCollection<ObservableCollection<SettingRow>> ItemsSourceOfDataGrid
{
get { return items; }
set
{
items = value;
RaisePropertyChangedEvent("ItemsSourceOfDataGrid")
}
}
EDIT: So after looking over the comments and what you want, the EASIEST thing to do is to define all of your row names and then have the ObservableCollection be a collection of columns instead of rows. If you want to go the crazy route of both dynamic rows and columns, well these can get you started:
https://svitla.com/blog/grid-with-dynamic-number-of-rows-and-columns-part-1
https://svitla.com/blog/grid-with-dynamic-number-of-rows-and-columns-part-2
The source Github: https://github.com/IReznykov/Blog
So, the way to do it with dynamic columns instead is pretty straight forward.
Just to reiterate:
Make sure your RaisePropertyChangedEvent is added to the items in your model. This will allow them to be changed dynamically inside your SettingsCollection. Also, the XAML portion of your DataGrid needs to be setup in a certain way to work with this.
public class SettingColumn : ObservableObject
{
public int _PhaseNumber;
public int PhaseNumber
{
get { return _PhaseNumber; }
set
{
PhaseNumber = value;
RaisePropertyChangedEvent("PhaseNumber");
}
}
public object _MinPhaseDuration;
public object MinPhaseDuration
{
get { return _Value; }
set
{
Value = value;
RaisePropertyChangedEvent("Value");
}
}
public string _MinSupplyAirTemp;
public string MinSupplyAirTemp
{
get { return _MinSupplyAirTemp; }
set
{
MinSupplyAirTemp = value;
RaisePropertyChangedEvent("MinSupplyAirTemp");
}
}
// The rest of your row labels here.
.
.
.
Hope this edit helps get you going. Again, dynamic rows AND columns is a pain. Save yourself some headache and define either all your row labels or your column labels.

Synchronizing two comboboxes bound to the same collection and to the same selected item

I have two comboboxes to represent a customer code and a customer name. Both are bound to the same collection of objects and to the same SelectedItem. I want to update the customer name when I select a customer code and vice versa.
I am using C# with the MVVM pattern. I have tried all the combination of SelectedItem and SelectedValue with selectedvaluepath but nothing seems to work.
These are my two combo boxes:
<ComboBox Name="CmbStockUnitCustomerCode" ItemsSource="{Binding CustomerCodeDtos}"
DisplayMemberPath="Code" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"></ComboBox>
<ComboBox Name="CmbStockUnitCustomerName" ItemsSource="{Binding CustomerCodeDtos}"
DisplayMemberPath="Name" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"></ComboBox>
And these are the bound objects:
public CustomerDto SelectedCustomer
{
get => _selectedcustomer;
set
{
_selectedcustomer = value;
RaisePropertyChanged("SelectedCustomer");
}
}
public class CustomerDto
{
public short Code { get; set; }
public string Name { get; set; }
public CustomerDto(short code, string name)
{
this.Code = code;
this.Name = name;
}
}
public ObservableCollection<CustomerDto> CustomerCodeDtos
{
get => _databaseService.GetAllCustomers();
}
When I update one of the comboboxes, I expect the other one to update to the corresponding value in the object CustomerDto, but nothing happens.
You're recreating your collection every time you reference it, so SelectedItem is referencing a different object. In effect, your two combo boxes are using different collections as ItemsSources. Change the code to
public ObservableCollection<CustomerDto> CustomerCodeDtos
{
get
{
if(_customerCodes==null)
{
_customerCodes = _databaseService.GetAllCustomers();
}
return _customerCodes;
}
}

WPF: Binding with two different lists

I have a few Problems with databinding in WPF.
I have a ListBox which has a binding to a BindingList.
<ListBox x:Name="SampleListBox" ItemsSource="{Binding List1}" ItemContainerStyle="{StaticResource ListBoxStyle}" BorderThickness="0" SelectedIndex="0" Margin="0">
<ListBox.ItemTemplate>
<DataTemplate >
<Border x:Name="border" Width="185">
<TextBlock Text="{Binding name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Till here, everything works fine. Now I have a DataGrid which should be linked to another BindingList and display some strings of it. So for example, if the first item of the ListBox is selected, the grid should show data of the first item of the second list.
I know, how it would work if both, the ListBox and the Grid get the data from the same list, but I have no idea, what to do, if this is not possible and there are two different lists.
You could bind SelectedIndex for the ListBox control to an property of type Int (Property1) in your ViewModel.
Also two-way bind SelectedItem in the DataGrid to another property (Property2) of the second list type.
In the setter for the Property1, change Property2 to be the item at the index of Property1 - i.e. List2[Property1]. It should change the selected item in the DataGrid.
So you want to use the listbox to, essentially, set a filter on the grid?
Note that LBItem and ViewModel below need to implement INotifyPropertyChanged and fire their PropertyChanged events when properties change, or none of this will work. But I'm leaving out the boilerplate for clarity.
Lots of ways to do that.
C#
public class LBItem {
public ViewModel Parent { get; set; }
public IEnumerable<String> SubItems {
get {
return Parent.List2.Where( /* filter items here */ );
}
}
}
public class ViewModel {
//
public ObservableCollection<LBItem> LBItems { get; set; }
public LBItem SelectedLBItem { get; set; }
public List<String> List2 { get; set; }
}
XAML
<ListBox
Name="MasterLB"
ItemsSource="{Binding LBItems}"
SelectedItem={Binding SelectedLBItem}"
/>
<DataGrid
ItemsSource="{Binding ElementName=MasterLB, Path=SelectedItem.SubItems}"
/>
That will work whether or not you bind MasterLB.SelectedItem to a property on the ViewModel. But as long as you are binding MasterLB.SelectedItem, you could just as easily bind DataGrid.ItemsSource to SelectedLBItem.SubItems on the ViewModel, like so:
<DataGrid
ItemsSource="{Binding Path=SelectedLBItem.SubItems}"
/>
But the ElementName binding is handy for a lot of things, so I'm giving you both.
You could also do something like this:
C#
public class LBItem {
public IEnumerable<String> Filter(IEnumerable<String> fullList) {
return fullList.Where( /* filter items here */ );
}
}
public class ViewModel {
public ObservableCollection<LBItem> LBItems { get; set; }
private LBItem _selectedItem;
public LBItem SelectedLBItem {
get { return _selectedItem; }
set {
_selectedItem = value;
List2Filtered = (null == _selectedItem)
? new List<String>()
: _selectedItem.Filter(List2).ToList();
}
}
public List<String> List2 { get; set; }
public List<String> List2Filtered { get; set; }
}
XAML
<ListBox
Name="MasterLB"
ItemsSource="{Binding LBItems}"
SelectedItem={Binding SelectedLBItem}"
/>
<DataGrid
ItemsSource="{Binding List2Filtered}"
/>

Pass variables to text boxes in C# WP8

I have a Pivotelement, so a Windows Phone Site with multiple sites and on every site should stand 5 menus. I have a List in which there every 5 menus and each menu of each site must be in a textbox:
//Site 1
textbox1_site1.text = list[0].menu1;
textbox2_site1.text = list[0].menu2;
...
//Site 2
textbox1_site2.text = list[1].menu1;
textbox2_site2.text = list[1].menu2;
...
//Site 3
textbox1_site3.text = list[2].menu1;
...
Here is where my list comes from.
public class Menus
{
public date Datum { get; set; }
public string menu1 { get; set; }
public string menu2 { get; set; }
...
}
public class list
{
public List<Menus> list { get; set; }
}
And I don't get it. how can I shorten this up in a loop like:
for(int i=0; i<5; i++)
{
textbox[i+1]_site[i+1] = list[i].menu1;
...
}
I know it's a beginner question and it's a little bit dumb but I don't get it.
from what i understand you have a list of values and want to display each value in a textbox. you can do this with a little data binding. You need to set the ItemsSource property of an ItemsControl to your data source (the list of values). After this all you have to do is modify the ItemsControl.ItemTemplate property. See if the code below helps. As you can see, everything is in the code-behind to keep it short.
//in ctor, after InitializeComponents()
DataContext = this;
//create the source of data (values to display in text boxes)
public List<string> Data{
get{
return new List<string>(){"item1", "item2"};
}
}
the rest is in the xaml file. Create an ItemsControl the gets the data from the Data property and displays it.
<ItemsControl ItemsSource="{Binding Data}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=TwoWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
this should solve your problem

Binding Datagrid row number

I have an object with an Order property I want to bind it in my datagrid. Is it possible ? I would like to change the order in a datagrid and use it in my object (With order property). Can I do it with binding ?
[Edit]
public class MyObject
{
...
public int Order {get; set;}
...
}
And I want to bind this property with Index of my row.
OK, still not really clear, but I'll try to provide some answer...
This is how I understand a question: you have a property of type Order', named 'Order' and want to edit this property in a data grid, for example using a Combo
Suppose you have an entity class like this (all code - just examples, because I don't know exact classes you have):
class Something : EntityBase {
................
public int Order { get; set; }
................
}
And it wrapped with data model like:
public SomethingModel : EntitydataModelBase<Something> {
................
public int Order {
get {
return this.Entity.Order;
}
set {
if (this.Entity.Order == value) return;
this.Entity.Order = value;
NotifyPropertyChanged("Order");
}
}
................
}
Also, there is a view model for a screen with your data grid, like this:
public SomethingListViewModel : ViewModelBase {
................
public IList<SomethingModel> _Items;
public IList<SomethingModel> Items {
get {
return _Items;
}
private set {
if (_Items == value) return;
_Items = value;
NotifyPropertyChanged("Items");
}
}
................
}
And finally your XAML:
................
<Grid x:Name="gridContainer"> <!-- We will use this name to make a reference in binding expression --!>
<DataGrid
ItemsSource="{Binding Items, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns>
<!-- "Actually, don't remember a name of class for tempolated column" -->
<DataGridTemplatedColumn
Header="Order">
<DataGridTemplatedColumn.CellTemplate>
<DataTemplate>
<TextBox
Text="{Binding Order, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplatedColumn.CellTemplate>
</DataGridTemplatedColumn>
</DataGrid.Columns>
</DataGrid>
................
Hope this will help, if it won't, please provide your example code, and I'll try to add some example based on it

Categories

Resources