i've a problem updating my datagrid when clicking the button by using NotifyPropertyChanged.
It works if i set the DataGrid.ItemsSource in code behind, but it doesn't if i set it in xaml.
here's some code of code behind & xaml:
namespace MyWpfDataBindingLab
{
public partial class NpcWindow : Window
{
DataCollection dc = new DataCollection();
public NpcWindow()
{
InitializeComponent();
//command binding code
//...
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//if i set the ItemsSource here, updating of the UI works
//dataGrid1.ItemsSource = dc;
}
private void CmdCollectionChangedExecute(object sender, ExecutedRoutedEventArgs e)
{
foreach (SampleClass s in dc)
{
s.Property1 = "changed";
s.Property3 = "changed";
s.Property3 = "changed";
break;
}
dc.Add(new SampleClass("new sample 1", "new sample 2", "new sample 3"));
}
}
}
<Window x:Class="WPFDataBinding.NpcWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:npc="clr-namespace:WPFDataBinding.NotifyPropChanged"
Title="MainWindow" Height="189" Width="459" Loaded="Window_Loaded">
<Window.Resources>
<npc:DataCollection x:Key="dataCol"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="349,110,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
<!-- if i set the ItemsSource here, updating of the UI doesn't work -->
<DataGrid ItemsSource="{Binding Source={StaticResource dataCol}, Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="True" Height="103" HorizontalAlignment="Left" Margin="12,12,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="331" />
</Grid>
my data & NotifyPropertyChanged implementation:
namespace MyWpfDataBindingLab.NotifyPropChanged
{
public class SampleClass : NotifyPropertyChanged
{
private string _field1;
private string _field2;
private string _field3;
public string Property1
{
get { return _field1; }
set
{
_field1 = value;
OnPropertyChanged("Property1");
}
}
public string Property2
{
get { return _field2; }
set
{
_field2 = value;
OnPropertyChanged("Property2");
}
}
public string Property3
{
get { return _field3; }
set
{
_field3 = value;
OnPropertyChanged("Property3");
}
}
public SampleClass()
{
_field1 = "value1";
_field2 = "value2";
_field3 = "value3";
}
public SampleClass(string p1, string p2, string p3)
{
_field1 = p1;
_field2 = p2;
_field3 = p3;
}
}
}
namespace MyWpfDataBindingLab.NotifyPropChanged
{
public abstract class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
namespace MyWpfDataBindingLab.NotifyPropChanged
{
public class DataCollection : ObservableCollection<SampleClass>
{
public DataCollection()
{
this.Add(new SampleClass());
this.Add(new SampleClass());
this.Add(new SampleClass());
}
}
}
i have no idea what the problem is. i'd appreciate it if someone can help solving my problem.
In your codebehind .xaml.cs create property
public ObservableCollection<SampleClass> MyCollection {get; set;}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//if i set the ItemsSource here, updating of the UI works
dataGrid1.ItemsSource = MyCollection;
}
In XAML:
<DataGrid ItemsSource="{Binding Path=., Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"/>
I followed up on syned's tip. For me I had to us the Mode as
Mode=TwoWay
The key here was the
UpdateSourceTrigger
property.
Thanks a lot...!
When you go crazy (like me) because it doesn't work this time (while it always worked!) Check the order of the calls. The OnPropertyChanged() must be called after the value was assigned.
public string Property1
{
get { return _field1; }
set
{
// assign the value first ...
_field1 = value;
// ... then call the property changed
OnPropertyChanged(nameof(Property1));
}
}
Related
I have a ViewModel with all the properties that i will need in every sub ViewModel.
It's the first time i try to split commands and viewmodel to multiple files. Last time everything was in the same ViewModel and it was a pain to work with it. Everything shows up as expected but i want to find a way to pass the same data in every viewmodel.
From my GetOrdersCommand, i want to get the HeaderViewModel.SelectedSource property. I didn't find any way to do it without getting a null return or loosing the property data...
I would like to call my GetOrdersCommand from HeaderView button too.
Any tips how i can achieve this ? Perhaps, my design is not good for what i'm trying to do ?
MainWindow.xaml
<views:HeaderView Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" DataContext="{Binding HeaderViewModel}" LoadHeaderViewCommand="{Binding LoadHeaderViewCommand}"/>
<TabControl TabStripPlacement="Bottom" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<TabItem Header="General">
</TabItem>
<TabItem Header="Orders">
<views:OrderView DataContext="{Binding OrderViewModel}" GetOrdersCommand="{Binding GetOrdersCommand}"/>
</TabItem>
</TabControl>
HeaderView.xaml
<DockPanel>
<ComboBox DockPanel.Dock="Left" Width="120" Margin="4" VerticalContentAlignment="Center" ItemsSource="{Binding SourceList}" SelectedItem="{Binding SelectedSource}" DisplayMemberPath="SourceName"/>
<Button x:Name="btnTest" HorizontalAlignment="Left" DockPanel.Dock="Left" Margin="4" Content="Test"/>
</DockPanel>
HeaderView.xaml.cs
public partial class OrderView : UserControl
{
public ICommand GetOrdersCommand
{
get { return (ICommand)GetValue(GetOrdersCommandProperty); }
set { SetValue(GetOrdersCommandProperty, value); }
}
public static readonly DependencyProperty GetOrdersCommandProperty =
DependencyProperty.Register("GetOrdersCommand", typeof(ICommand), typeof(OrderView), new PropertyMetadata(null));
public OrderView()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (GetOrdersCommand != null)
{
GetOrdersCommand.Execute(this);
}
}
}
MainViewModel.cs
private OrderViewModel orderViewModel;
public OrderViewModel OrderViewModel { get; set; } // Getter, setter with OnPropertyChanged
private HeaderViewModel headerViewModel;
public HeaderViewModel HeaderViewModel { get; set; } // Getter, setter with OnPropertyChanged
public MainViewModel()
{
HeaderViewModel = new HeaderViewModel();
OrderViewModel = new OrderViewModel();
}
HeaderViewModel.cs
public ICommand LoadHeaderViewCommand { get; set; }
public HeaderViewModel()
{
LoadHeaderViewCommand = new LoadHeaderViewCommand(this);
}
GetOrdersCommand.cs
public class GetOrdersCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly OrderViewModel _orderViewModel;
public GetOrdersCommand(OrderViewModel orderViewModel)
{
_orderViewModel = orderViewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
/* Build Order List according to HeaderViewModel.SelectedSource */
_orderViewModel.Orders = new ObservableCollection<Order>()
{
new Order { ID = 1, IsReleased = false, Name = "Test1"},
new Order { ID = 2, IsReleased = true, Name = "Test2"},
};
}
}
Thanks guys ! I moved my commands to their owning ViewModel as suggested.
I tried MVVVM Light Tools and found about Messenger Class.
I used it to send my SelectedSource (Combobox from HeaderView) from HeaderViewModel to OrderViewModel. Am i suppose to use Messenger class like that ? I don't know, but it did the trick!!!
I thought about moving GetOrdersCommand to OrderViewModel, binding my button command to OrderViewModel, binding SelectedSource as CommandParameter but i didn't know how i was suppose to RaiseCanExecuteChanged when HeaderViewModel.SelectedSource changed... Any advice?
MainWindow.xaml
<views:HeaderView DataContext="{Binding Source={StaticResource Locator}, Path=HeaderVM}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"/>
<TabControl TabStripPlacement="Bottom" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<TabItem Header="General">
</TabItem>
<TabItem Header="Orders">
<views:OrderView DataContext="{Binding Source={StaticResource Locator}, Path=OrderVM}"/>
</TabItem>
</TabControl>
OrderViewModel.cs
private ObservableCollection<Order> _orders;
public ObservableCollection<Order> Orders
{
get { return _orders; }
set
{
if (_orders != value)
{
_orders = value;
RaisePropertyChanged(nameof(Orders));
}
}
}
public OrderViewModel()
{
Messenger.Default.Register<Source>(this, source => GetOrders(source));
}
private void GetOrders(Source source)
{
if (source.SourceName == "Production")
{
Orders = new ObservableCollection<Order>(){
new Order { ID = 1, IsReleased = false, Name = "Production 1" }
};
}
else
{
Orders = new ObservableCollection<Order>(){
new Order { ID = 2, IsReleased = true, Name = "Test 1" }
};
}
}
Part of HeaderViewModel.cs
private Source _SelectedSource;
public Source SelectedSource
{
get { return _SelectedSource; }
set
{
if (_SelectedSource != value)
{
_SelectedSource = value;
RaisePropertyChanged(nameof(SelectedSource));
GetOrdersCommand.RaiseCanExecuteChanged();
}
}
}
private RelayCommand _GetOrdersCommand;
public RelayCommand GetOrdersCommand
{
get
{
if (_GetOrdersCommand == null)
{
_GetOrdersCommand = new RelayCommand(GetOrders_Execute, GetOrders_CanExecute);
}
return _GetOrdersCommand;
}
}
private void GetOrders_Execute()
{
Messenger.Default.Send(SelectedSource);
}
private bool GetOrders_CanExecute()
{
return SelectedSource != null ? true : false;
}
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}"/>
The 2nd column items "Point Setting" don't show up until the 1st column items are sorted, clicking on the header of the 1st column. The goal of this code is to link the 1st and 2nd column items, then use the 2nd column items as the search keys.
I'm new to C# and WPF.
I tired to put sequential numbers in front of the 1st column items (1., 2., and so on) because I thought it would solve the problem if those items are initially sorted. But, no luck. I heard that ObservableCollection<> doesn't manage the input order, so once I changed it with List<>. But it didn't solve this problem, too.
Actually, I don't want to sort the 1st column; they should be fixed and no need to change the order/number at all.
To avoid any confusions, let me show my 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>
</Window>
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 comboBox = sender as ComboBox;
var selectedPoint = comboBox.SelectedItem as Point;
tb.Text = selectedPoint.name;
}
}
public class WakeupClass
{
public event EventHandler BrowseFile;
public void Start()
{
BrowseFile(this, EventArgs.Empty);
}
}
}
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));
}
}
}
}
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");
}
}
}
}
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 = "1. High"},
new Record {name = "2. Middle"},
new Record {name = "3. Low"}
};
}
internal void ExcelFileOpen(object sender, System.EventArgs e)
{
points = new ObservableCollection<Point> { new Point { } };
MessageBox.Show("Please assume that Excel data are loaded here.");
points.Add(new Point { name = "point1", code = 1 });
points.Add(new Point { name = "point2", code = 2 });
points.Add(new Point { name = "point3", code = 3 });
points.Add(new Point { name = "point4", code = 4 });
}
}
}
The procedure goes like:
Click on the "Browse" button to load the data.
Click on the 1st column "Channel" to sort the list (GOAL: I'd like to GET RID OF this step).
Click on the "Point Setting" ComboBox to select the items (point1, point2, ..., etc.).
... I don't know if ObservableCollection<> is appropriate here. If List<> or any other type is better, please change it. Any suggestion would be helpful. Thank you in advance.
Change your points ObservableCollection like such, because you're setting the reference of the collection after the UI is rendered, you would need to trigger the PropertyChanged event to update the UI.
private ObservableCollection<Point> _points;
public ObservableCollection<Point> points
{
get { return _points; }
set
{
_points = value;
OnPropertyChanged(nameof(points));
}
}
An alternative would be to first initialise your collection.
public ObservableCollection<Point> points { get; set; } = new ObservableCollection<Point>();
internal void ExcelFileOpen(object sender, System.EventArgs e)
{
// Do not re-initialise the collection anymore.
//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 });
}
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.
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>