Binding ComboBox SelectedItem to Datagrid - c#

Having some problems displaying strings in a datagrid.
To explain the code: I am binding a collection of Soldiers to a ComboBox. A Soldier has its own collection of weapons.
When I select a specific soldier in the ComboBox, I want that soldier's weapons displayed in the datagrid. I believe I'm binding correctly, but the datagrid always comes up blank. Anybody know what i'm doing wrong?
XAML
<Grid>
<ComboBox x:Name="Character_ComboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="328" Height="25">
</ComboBox>
</Grid>
<DataGrid x:Name="Character_items_datagrid" ItemsSource="{Binding ElementName=Character_ComboBox, Path= SelectedItem.Equipment, Mode=OneWay}" Margin="328,0,0,0" Grid.RowSpan="2" >
<DataGrid.Columns>
<DataGridTextColumn Header="Primary" Binding="{Binding Primary, Mode=TwoWay}" FontWeight="Bold" Foreground="Black" Width="0.1*"></DataGridTextColumn>
<DataGridTextColumn Header ="Secondary" Binding="{Binding Secondary, Mode=TwoWay}" Width="0.1*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Soldier Class
public class Soldier
{
public string Soldier_Class { get; set; }
public ObservableCollection<Weapons> Equipment { get; set; }
}
Weapons Class
public class Weapons
{
string Primary { get; set; }
string Secondary { get; set; }
public Weapons(string primary, string secondary)
{
this.Primary = primary;
this.Secondary = secondary;
}
}
MainWindow
public ObservableCollection<Soldier> squad_members = new ObservableCollection<Soldier>();
public MainWindow()
{
InitializeComponent();
squad_members.Add(new Soldier() { Soldier_Class = "Assult Soldier", Equipment = new ObservableCollection<Weapons>() { new Weapons("M4 Rifle", "Compact 45 Pistol")}});
squad_members.Add(new Soldier() { Soldier_Class = "SMG Soldier", Equipment = new ObservableCollection<Weapons>() { new Weapons("RPK Machine Gun", "HK Shotgun"), new Weapons("SAW Machine Gun", "Compact 45 Pistol")}});
squad_members.Add(new Soldier() { Soldier_Class = "Juggernaut", Equipment = new ObservableCollection<Weapons>() { new Weapons("MP5", "Bowie Knife") }});
Binding comboBinding = new Binding();
comboBinding.Source = squad_members;
BindingOperations.SetBinding(Character_ComboBox, ComboBox.ItemsSourceProperty, comboBinding);
Character_ComboBox.DisplayMemberPath = "Soldier_Class";
Character_ComboBox.SelectedValuePath = "Soldier_Class";
}
Result:

You need to make properties in the model public for binding to be able to work :
public class Weapons
{
public string Primary { get; set; }
public string Secondary { get; set; }
.....
}
Your DataGrid looks populated with items correctly, just the properties of each item are not correctly displayed in the columns. This is indication that binding engine can't access the item's properties due to it's private accessibility.

Your primary problem is the public access modifier, as har07 wrote.
There are a lot of other things you can improve as well. Implement INotifyPropertyChanged for your classes, so any change to the properties is immediately reflected by the UI. Without compelling reasons, do not create bindings in code. Use a ViewModel to bind to, instead of binding directly to elements like ComboBox.SelectedItem. Set AutoGenerateColumns to false if you want to style your columns (your code would produce four columns). Use Grid.ColumnDefinitions instead of assigning a fixed margin.
Models:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApplication1.ViewModels
{
public class SquadViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<Soldier> _squadMembers;
public ObservableCollection<Soldier> SquadMembers { get { return _squadMembers; } set { _squadMembers = value; OnPropertyChanged("SquadMembers"); } }
private Soldier _selectedSoldier;
public Soldier SelectedSoldier { get { return _selectedSoldier; } set { _selectedSoldier = value; OnPropertyChanged("SelectedSoldier"); } }
public SquadViewModel()
{
SquadMembers = new ObservableCollection<Soldier>()
{
new Soldier() { SoldierClass = "Assult Soldier", Equipment = new ObservableCollection<Weapon>() { new Weapon("M4 Rifle", "Compact 45 Pistol") } },
new Soldier() { SoldierClass = "SMG Soldier", Equipment = new ObservableCollection<Weapon>() { new Weapon("RPK Machine Gun", "HK Shotgun"), new Weapon("SAW Machine Gun", "Compact 45 Pistol") } },
new Soldier() { SoldierClass = "Juggernaut", Equipment = new ObservableCollection<Weapon>() { new Weapon("MP5", "Bowie Knife") } }
};
}
}
public class Soldier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _soldierClass;
public string SoldierClass { get { return _soldierClass; } set { _soldierClass = value; OnPropertyChanged("SoldierClass"); } }
private ObservableCollection<Weapon> _equipment;
public ObservableCollection<Weapon> Equipment { get { return _equipment; } set { _equipment = value; OnPropertyChanged("Equipment"); } }
}
public class Weapon : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _primary;
string Primary { get { return _primary; } set { _primary = value; OnPropertyChanged("Primary"); } }
private string _secondary;
string Secondary { get { return _secondary; } set { _secondary = value; OnPropertyChanged("Secondary"); } }
public Weapon(string primary, string secondary)
{
this.Primary = primary;
this.Secondary = secondary;
}
}
}
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
Title="MainWindow" Height="350" Width="580">
<Window.DataContext>
<vm:SquadViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="CbxCharacter" HorizontalAlignment="Left" VerticalAlignment="Top" Width="328" Height="25"
ItemsSource="{Binding SquadMembers}" SelectedItem="{Binding SelectedSoldier}"
DisplayMemberPath="SoldierClass" SelectedValuePath="SoldierClass"/>
<DataGrid x:Name="DgCharacterItems" ItemsSource="{Binding SelectedSoldier.Equipment, Mode=OneWay}" Grid.Column="1" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Primary" Binding="{Binding Primary, Mode=TwoWay}" FontWeight="Bold" Foreground="Black" Width="*" />
<DataGridTextColumn Header="Secondary" Binding="{Binding Secondary, Mode=TwoWay}" Width="*" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

Related

How to send gridview selected row data to another window with textboxes in wpf using mvvm

I have tried a bunch of solutions about this problem on google but none seem to be helpful.
I have a button on every row which when clicked open a new window with textboxes. This window should display the selected row cells data.
I load the datagrid from mysql database.
VIEW
textboxes (XML) for second window
<Label Content="{Binding sFirstName, Mode=OneWay }" /> <Label Content="{Binding sLastName, Mode=OneWay }" />
Datagrid
<DataGrid ItemsSource="{Binding Path=MM}" SelectionMode="Extended" SelectedItem="{Binding SelectedItem}" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=sFirstName}" />
<DataGridTextColumn Binding="{Binding Path=sLastName}" />
</DataGrid.Columns>
MODEL
public class MM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string PropertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName)); }
private string _sFirstName, _sLastName;
public string sFirstName { get { return _sFirstName; } set { if (_sFirstName != value) { _sFirstName = value; OnPropertyChanged("sFirstName"); } } }
public string sLastName { get { return _sLastName; } set { if (_sLastName != value) { _sLastName = value; OnPropertyChanged("sLastName"); } } }
public DataRowView SelectedRow
{
get { return SelectedRow; }
set { SelectedRow = value; OnPropertyChanged("SelectedItem"); }
}
}
VIEW MODEL
Public class MV : INotifyPropertyChanged
{
private ICommand cmdLoad;
public ICommand CmdLoad { get { if (cmdLoad == null) cmdLoad = new RelayCommand(p => OnLoad()); return cmdLoad; } }
private void OnLoad() { Load(); }
public ObservableCollection<FinTuitionM> finTuitionM { get; set; }
public ClrIdVMD()
{
Load();
}
public void Load()
{
}
}
Code behind (cs)
public partial class Home : Window
{
MV mv;
public Home()
{ InitializeComponent();
mv = new MV(); DataContext = mv;
}
}
You seem to be very confused, so I have prepared a small example of what I think you are trying to achieve.
I am guessing that you want to have a main view that is essentially read only, and you intend to use a popup to make changes. On this basis the View Model for the main window does not need to implement INotifyPropertyChanged. So a simple View Model would look like this:
public class MV
{
public ObservableCollection<MM> MMs { get; set; }
private ICommand cmdShowDetails;
public ICommand CmdShowDetails
{
get
{
if (cmdShowDetails == null) cmdShowDetails = new RelayCommand(p => ShowDetails());
return cmdShowDetails;
}
}
public void ShowDetails()
{
var detVM = new DetailsVM(SelectedItem);
var dets = new DetailsWindow(detVM);
dets.ShowDialog();
}
public MV()
{
MMs = new ObservableCollection<MM>
{
new MM{sFirstName = "Mickey", sLastName = "Mouse"},
new MM{sFirstName = "Donald", sLastName = "Duck"},
new MM{sFirstName = "Roger", sLastName = "Rabbit"},
};
}
public MM SelectedItem { get; set; }
}
Notice that for demonstration purposes, I have loaded the ObservableCollection with some dummy data. In your case, this is replaced with data from the database.
The MM class that this refers to then looks something like this:
public class MM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, e);
}
}
private string firstName;
public string sFirstName
{
get { return firstName; }
set
{
if (firstName == value)
{
return;
}
firstName = value;
RaisePropertyChangedEvent("sFirstName");
}
}
private string lastName;
public string sLastName
{
get { return lastName; }
set
{
if (lastName == value)
{
return;
}
lastName = value;
RaisePropertyChangedEvent("sLastName");
}
}
}
Notice that SelectedItem is in the View Model (MV) and is an object of class MM, so that when the second window is opened, the ShowDetails command can pass the selected details.
This therefore calls for a new very simple view model for the second (details) window:
public class DetailsVM
{
public MM Detail { get; set; }
public DetailsVM(MM detail)
{
Detail = detail;
}
}
The main window grid xaml now looks like this:
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Button Content="Show Details" Command="{Binding CmdShowDetails}"></Button>
</StackPanel>
<DataGrid ItemsSource="{Binding MMs}" SelectedItem="{Binding SelectedItem}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding sFirstName}" />
<DataGridTextColumn Header="Last Name" Binding="{Binding sLastName}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Grid>
Notice here that I only have one button at the bottom of the window to transfer the details. This is because the details come from the selected item, which is the highlighted row.
The code behind is simply:
public partial class MainWindow : Window
{
private MV _mV;
public MainWindow()
{
InitializeComponent();
_mV = new MV();
DataContext = _mV;
}
}
Finally the xaml for the second (details) window
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40*"/>
<RowDefinition Height="40*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70*"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Label Content="First Name" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Text="{Binding Detail.sFirstName}" Grid.Column="1" Grid.Row="0" Width="150" Height="25" HorizontalAlignment="Left" />
<Label Content="Last Name" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Text="{Binding Detail.sLastName}" Grid.Column="1" Grid.Row="1" Width="150" Height="25" HorizontalAlignment="Left" />
</Grid>
Notice here that the binding is to Detail.sFirstName and Detail.sLastName. The DataContext is a DetailsVM object, which has a property Detail of type MM, hence sFirstName and sLastName are sub-properties of Detail.
This window also has a very simple code behind:
public partial class DetailsWindow : Window
{
private DetailsVM _details;
public DetailsWindow(DetailsVM details)
{
_details = details;
DataContext = _details;
InitializeComponent();
}
}
If you now run this, you will find that changes made in the second window are automatically reflected back into the main window. In practice you will probably want Save and Cancel buttons in the second window.
I hope the above is sufficient to point you in the right direction!

Cannot get selected ComboBox items in MainWindow.xaml.cs

I'd like to access ComboBox items (which are defined in another class) in MainWindow.xaml.cs, but I can't.
I'm new to C# and WPF. The purpose of this code is to get a selected ComboBox item as a search key. I have copied from many example codes on the Internet and now I'm completely lost. I don't even know which part is wrong. So, let me show the entire codes (sorry):
MainWindow.xaml:
<Window x:Class="XY.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XY"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="454.4">
<Grid>
<DataGrid ItemsSource="{Binding channels}"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
Margin="0,0,0,-0.2">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding name}"
Header="Channel" Width="Auto"/>
<DataGridTemplateColumn Width="100" Header="Point Setting">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="piontsComboBox"
ItemsSource="{Binding DataContext.points,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
SelectionChanged="PrintText"
DisplayMemberPath="name"
SelectedValuePath="name"
Margin="5"
SelectedItem="{Binding DataContext.SelectedPoint,
RelativeSource={RelativeSource AncestorType={x:Type Window}},
Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<TextBox x:Name="tb" Width="140" Height="30" Margin="10,250,200,30"></TextBox>
<Button x:Name="Browse_Button" Content="Browse" Margin="169,255,129.6,0"
Width="75" Click="Browse_Button_Click" Height="30" VerticalAlignment="Top"/>
</Grid>
MainWindow.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace XY
{
public partial class MainWindow : Window
{
public GridModel gridModel { get; set; }
public MainWindow()
{
InitializeComponent();
gridModel = new GridModel();
this.DataContext = gridModel;
}
private void Browse_Button_Click(object sender, RoutedEventArgs e)
{
WakeupClass clsWakeup = new WakeupClass();
clsWakeup.BrowseFile += new EventHandler(gridModel.ExcelFileOpen);
clsWakeup.Start();
}
void PrintText(object sender, SelectionChangedEventArgs args)
{
//var item = pointsComboBox SelectedItem as Point;
//if(item != null)
//{
// tb.Text = "You selected " + item.name + ".";
//}
MessageBox.Show("I'd like to show the item.name in the TextBox.");
}
}
public class WakeupClass
{
public event EventHandler BrowseFile;
public void Start()
{
BrowseFile(this, EventArgs.Empty);
}
}
}
Point.cs:
namespace XY
{
public class Point : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _code;
public int code
{
get { return _code; }
set
{
_code = value;
OnPropertyChanged("code");
}
}
}
}
Record.cs:
namespace XY
{
public class Record : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _PointCode;
public int PointCode
{
get { return _PointCode; }
set
{
_PointCode = value;
OnPropertyChanged("PointCode");
}
}
private Record _selectedRow;
public Record selectedRow
{
get { return _selectedRow; }
set
{
_selectedRow = value;
OnPropertyChanged("SelectedRow");
}
}
private Point _selectedPoint;
public Point SelectedPoint
{
get { return _selectedPoint; }
set
{
_selectedPoint = value;
_selectedRow.PointCode = _selectedPoint.code;
OnPropertyChanged("SelectedRow");
}
}
}
}
ViewModelBase.cs:
using System.ComponentModel;
namespace XY
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
GridModel.cs:
using System.Collections.ObjectModel;
using System.Windows;
namespace XY
{
public class GridModel : ViewModelBase
{
public ObservableCollection<Record> channels { get; set; }
public ObservableCollection<Point> points { get; set; }
public GridModel()
{
channels = new ObservableCollection<Record>() {
new Record {name = "High"},
new Record {name = "Middle"},
new Record {name = "Low"}
};
}
internal void ExcelFileOpen(object sender, System.EventArgs e)
{
points = new ObservableCollection<Point> { new Point { } };
points.Add(new Point { name = "point1", code = 1 });
points.Add(new Point { name = "point2", code = 2 });
points.Add(new Point { name = "point3", code = 3 });
MessageBox.Show("Assume that Excel data are loaded here.");
}
}
}
The procedure goes like:
Click on the "Browse" button to load the data.
Click on the 1st column "Channel" to sort the list (This is a bug, but if you don't, the "Point Setting" items won't show up).
Click on the "Point Setting" ComboBox to select the items (point1, point2, ..., etc.).
This code is supposed to show the selected item name in the TextBox.
If everything is in MainWindow.xaml.cs, the ComboBox items could be accessed. Since I split the codes into different classes, it has not been working. Please help me. Any suggestion would be helpful.
Your binding does work. You can make use of the sender object to achieve what you wanted.
void PrintText(object sender, SelectionChangedEventArgs args)
{
var comboBox = sender as ComboBox;
var selectedPoint = comboBox.SelectedItem as Point;
tb.Text = selectedPoint.name;
}
The problem is that the DataGridColumn is not part of the WPF logical tree and so your relative source binding will not work. The only way to get your binding to work is a type of kluge (very common with WPF in my experience). Create a dummy element that is in the logical tree and then reference that.
So
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid ItemsSource="{Binding channels}"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
Margin="0,0,0,-0.2">
Then your binding will look like this
<ComboBox x:Name="piontsComboBox"
ItemsSource="{Binding DataContext.points,
Source={x:Reference dummyElement}}"
SelectionChanged="PrintText"
DisplayMemberPath="name"
SelectedValuePath="name"
Margin="5"
SelectedItem="{Binding DataContext.SelectedPoint,
Source={x:Reference dummyElement},
Mode=TwoWay}"/>

Updating ComboBox in XAML without Somegrid.Items.Refresh()

I'm new in WPF+C# and starting to get a grip on it.
I want to update ComboBox inside DataGrid when program is running.
Unfortunately, ComboBox never show any selected item if I'm using Somegrid.Item.Refresh() to update DataGrid.
Here is the XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid Name="Somegrid" HorizontalAlignment="Left" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<TextBlock Text="SomebodY" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Somebody}" SelectionChanged="ComboBox_SelectionChanged_Somebody" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<TextBlock Text="SomethinG" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Something}" SelectionChanged="ComboBox_SelectionChanged_Something" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<TextBlock Text="SomewherE" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Somewhere}" SelectionChanged="ComboBox_SelectionChanged_Somewhere" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Behind code
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
someclassFoo.Somebody = new List<string>() { "Alan", "Benjamin" };
observableSomeclassFoo.Add(someclassFoo);
Somegrid.ItemsSource = observableSomeclassFoo;
}
public SomeclassFoo someclassFoo = new SomeclassFoo();
private ObservableCollection<SomeclassFoo> _observableSomeclassFoo = new ObservableCollection<SomeclassFoo>();
public ObservableCollection<SomeclassFoo> observableSomeclassFoo { get { return _observableSomeclassFoo; } }
private void ComboBox_SelectionChanged_Somebody(object sender, RoutedEventArgs e)
{
System.Windows.Controls.ComboBox cb1 = (System.Windows.Controls.ComboBox)sender;
if (cb1.SelectedItem.ToString() == "Alan")
{
someclassFoo.Something = new List<string>() { "Apple" };
}
else if (cb1.SelectedItem.ToString() == "Benjamin")
{
someclassFoo.Something = new List<string>() { "Banana" };
}
//Somegrid.Items.Refresh();
}
private void ComboBox_SelectionChanged_Something(object sender, RoutedEventArgs e)
{
System.Windows.Controls.ComboBox cb2 = (System.Windows.Controls.ComboBox)sender;
if (cb2.SelectedItem.ToString() == "Apple")
{
someclassFoo.Somewhere = new List<string>() { "Antartica" };
}
else if (cb2.SelectedItem.ToString() == "Banana")
{
someclassFoo.Somewhere = new List<string>() { "Bedrock" };
}
//Somegrid.Items.Refresh();
}
private void ComboBox_SelectionChanged_Somewhere(object sender, RoutedEventArgs e)
{
System.Windows.Controls.ComboBox cb3 = (System.Windows.Controls.ComboBox)sender;
MessageBox.Show(cb3.SelectedItem.ToString().ToUpper() + " selected");
}
}
}
Another class
using System.Collections.Generic;
namespace WpfApplication1
{
public class SomeclassFoo
{
public IList<string> Somebody { get; set; }
public IList<string> Something { get; set; }
public IList<string> Somewhere { get; set; }
}
}
How I can update the ComboBox SomethinG and ComboBox SomewherE without using Somegrid.Item.Refresh() ?
The SomeclassFoo type should implement the INotifyPropertyChanged interface and raise change notifications:
public class SomeclassFoo : INotifyPropertyChanged
{
private IList<string> _somebody;
public IList<string> Somebody
{
get { return _somebody; }
set { _somebody = value; NotifyPropertyChanged(); }
}
private IList<string> _something;
public IList<string> Something
{
get { return _something; }
set { _something = value; NotifyPropertyChanged(); }
}
private IList<string> _someWhere;
public IList<string> Somewhere
{
get { return _someWhere; }
set { _someWhere = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
How to: Implement Property Change Notification: https://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx
Instead of refreshing the grid, remove the current source and set a new source:
yourGrid.ItemsSource = null;
yourGrid.ItemsSource = yourDataSource;

How to bind to a nested class wpf

I have a view model called FieldViewModel:
public class FieldViewModel: INotifyPropertyChanged
{
private Field m_field;
public Field FieldData
{
get { return m_field; }
set
{
m_field = value;
NotifyPropertyChanged("FieldData");
}
}
and the Field class:
public class Field : INotifyPropertyChanged
{
public List<string> SqlTypes
{
get { return Enum.GetNames(typeof(SqlDbType)).ToList(); }
}
private string m_FieldName = null;
public string FieldName
{
get { return m_FieldName; }
set
{
m_FieldName = value;
NotifyPropertyChanged("FieldName");
}
}
private string m_FieldType = null;
public string FieldType
{
get { return m_FieldType; }
set
{
m_FieldType = value;
NotifyPropertyChanged("FieldType");
}
}
private bool m_NullAllow = false;
public bool NullAllow
{
get { return m_NullAllow; }
set
{
m_NullAllow = value;
NotifyPropertyChanged("NullAllowsChecked");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
The Xaml:
<ComboBox ItemsSource="{Binding FieldData.SqlTypes}" SelectionChanged="cmbField_Selected" Width="20" Height="20" Margin="5,5,0,5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<TextBox Width="120" Height="20" Text="{Binding FieldType}" Margin="0,5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
now in the xaml i want to bind the variables of the Field Class but i can't see them
I tried some thing but nothing works, if i put the variables of the Field Class inside the FieldViewModel it works good.
Thank for your help.
Here is an example which works for me:
<Window x:Class="DataGridExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="czesciTable" ItemsSource="{Binding model.list}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Viewmodel:
public class ViewModel
{
public Model model { get; set; }
public ViewModel()
{
model = new Model();
}
}
And model:
public class Model
{
public List<String> list { get; set; }
public Model()
{
list = new List<string>();
list.Add("Item1");
list.Add("Item2");
list.Add("Item3");
}
}
Result:
Or ComboBox:
<ComboBox ItemsSource="{Binding model.list}" VerticalAlignment="Top"/>
The code in the question was mainly correct. It was only missing the initialization of the Field Class.

EASY way to refresh ListBox in WPF?

I have created a simple form that inserts/updates/deletes a values for Northwind Customers.
Everything works fine, except in order to see a results, I have to close it, and reopen again.
My form looks like this :
I've searched tens of articles on how to refresh ListBox, but all of those use interface implementing, or using DataSets, and stuff I have never heard of and cannot implement. It's a very simple project, using simple procedures. Is there an easy way to refresh the list of customers without adding many lines of code?
The simple answer is: myListBox.Items.Refresh();
Are you using ObservableCollection and does your model implement INotifyPropertyChanged these two things will automaticly update the ListBox on any change. no need to explicitly refresh the list.
Here is a small example of using ObservableCollection and INotifyPropertyChanged, obviously you will populate your ObservableCollection from your SQL database.
Window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<MyModel> _list = new ObservableCollection<MyModel>();
private MyModel _selectedModel;
public MainWindow()
{
InitializeComponent();
List.Add(new MyModel { Name = "James", CompanyName = "StackOverflow"});
List.Add(new MyModel { Name = "Adam", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Chris", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Steve", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Brent", CompanyName = "StackOverflow" });
}
public ObservableCollection<MyModel> List
{
get { return _list; }
set { _list = value; }
}
public MyModel SelectedModel
{
get { return _selectedModel; }
set { _selectedModel = value; NotifyPropertyChanged("SelectedModel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Name="UI">
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=List}" SelectedItem="{Binding ElementName=UI, Path=SelectedModel}" Margin="0,0,200,0" DisplayMemberPath="DisplayMember" SelectedIndex="0" />
<StackPanel HorizontalAlignment="Left" Height="100" Margin="322,10,0,0" VerticalAlignment="Top" Width="185">
<TextBlock Text="Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Company Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.CompanyName, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
Model
public class MyModel : INotifyPropertyChanged
{
private string _name;
private string _companyName;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public string CompanyName
{
get { return _companyName; }
set { _companyName = value; NotifyPropertyChanged("CompanyName"); }
}
public string DisplayMember
{
get { return string.Format("{0} ({1})", Name, CompanyName); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("DisplayMember"));
}
}
}
In this case any edit to properties will Update your list instantly, also will update when new Items are added/removed.
How about calling ListBox.UpdateLayout?
Of course you also need to update the particular item(s) so that it returns the updated string from the ToString method.
UPDATE: I think you also need to call ListBox.InvalidateArrange before you call ListBox.UpdateLayout.
Use INotifyPropertyChanged is the best way, refresh the entire list is not a good idea.
Main entrance:
public partial class MainWindow : Window
{
private BindingList<FoodModel> foodList = new BindingList<FoodModel>();
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
foodList.Add(new FoodModel { foodName = "apple1" });
foodList.Add(new FoodModel { foodName = "apple2" });
foodList.Add(new FoodModel { foodName = "apple3" });
FoodListBox.ItemsSource = foodList;
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
foodList[0].foodName = "orange";
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
FoodListBox.Items.Refresh();
}
}
Model:
public class FoodModel: INotifyPropertyChanged
{
private string _foodName;
public string foodName
{
get { return _foodName; }
set
{
if (_foodName != value)
{
_foodName = value;
PropertyChanged(this, new PropertyChangedEventArgs("foodName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
XAML:
<ListBox HorizontalAlignment="Center" Name="FoodListBox" VerticalAlignment="Top" Width="194" Height="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding foodName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Categories