Provide a Collection as source to property in WPF Propertygrid - wpf

I have the following property of type string.
[Category("General")]
[DisplayName("Book Name")]
public string BookName
{ //getter;
//setter;
}
When binding an object containing this property to propertygrid, I would like to provide a list of type string as source.
List<string> booksource = new List<string>();
When Property is of type enum, it automatically populates combobox, I want to acheive same functionality through collection.
Edit:
Expanded:
enum BookType
{
Novel = 0,
Magazine = 1
}
class Class1
{
string _bookname = "Book 1";
BookType _booktype = BookType.Magazine;
[Category("General")]
[DisplayName("Book Name")]
public string BookName
{
get { return this._bookname; }
set { this._bookname = value; }
}
[Category("General")]
[DisplayName("Book Type")]
public BookType BookType
{
get { return this._booktype; }
set { this._booktype = value; }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Class1 obj = new Class1();
this.wpfpropertygrid.SelectedObject = obj;
}
}
For the above code, the propertygrid displays a combobox with items "Magazine" and "Novel" for property BookType and a textbox with text "Book 1" for property BookName. I want the property BookName displayed as combobox to which i can explicitly provide a source. I would like to bind a list {"Book 1","Book 2","Book 3"} to property BookName, so that the user can select any one of them.

Better late than never ;-)
With PropertyGrid from Extended WPF Toolkit you can do this that way:
enum BookType
{
Novel = 0,
Magazine = 1
}
public class BookItemsSource : IItemsSource
{
public ItemCollection GetValues()
{
var books = new ItemCollection();
books.Add("Book 1");
books.Add("Book 2");
books.Add("Book 3");
return books;
}
}
public class Class1
{
string _bookname = "Book 1";
BookType _booktype = BookType.Magazine;
[Category("General")]
[DisplayName("Book Name")]
[ItemsSource(typeof(BookItemsSource))]
public string BookName
{
get { return this._bookname; }
set { this._bookname = value; }
}
[Category("General")]
[DisplayName("Book Type")]
public BookType BookType
{
get { return this._booktype; }
set { this._booktype = value; }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Class1 obj = new Class1();
this.wpfpropertygrid.SelectedObject = obj;
}
}

Related

RaisePropertyChanged not updating properties when function called from another viewmodel

This class is used to bind search result in listbox and onselected list it will show result in overview panel, both are two different view model.
public class SearchClients : Client
{
public Client Client;
private void SelectedClient(int PartyId)
{
List<Client> c = this.fileService.FindClients(PartyId, "", "").ToList();
if (c.Count > 0)
{
Client = c[0];
}
OverviewPageViewModel viewModel = this.injector.Resolve<OverviewPageViewModel>("OverviewPage");
viewModel.SelectedClient(Client);
}
}
Search Panel View Model
public class SearchBar : BaseContentViewModel
{
private void FindClients()
{
List<Client> C = fileService.FindClients(0, SrchFirstName, SrchLastName).ToList();
}
public ICommand FindClient
{
get
{
return new RelayCommand(FindClients);
}
}
}
Result Panel View Model
public class OverviewPageViewModel: BaseContentViewModel
{
public void SelectedClient(Client Client)
{
Title = Client.TitleID;
FirstName = Client.FirstName;
LastName = Client.LastName;
}
}
ViewModel was assigned base class IContentViewModel in tab navigation, it should be initiated with OverviewPageViewModel which have all properties. Solved!!
Wrong:
IContentViewModel viewModel = injector.Resolve<IContentViewModel>(TabPage);
Correct:
IContentViewModel viewModel = injector.Resolve<IContentViewModel>(TabPage);
if (TabPage == "OverviewPage")
{
injector.Resolve<ViewModels.Windows.MainWindowViewModel>().CurrentPageViewModel = injector.Resolve<OverviewPageViewModel>(TabPage);
}
else
{
//viewModel.ClearData();
injector.Resolve<ViewModels.Windows.MainWindowViewModel>().CurrentPageViewModel = viewModel;
}

Binding to ItemsSource of ListBox in a UserControl using Dependency Property

I'm facing this weird problem.It looks like well known question. I tried to find the solution. It took one whole day. But still no use. All the solutions I tried didn't help me.
<ListBox ItemsSource="{Binding ElementName=root,Path=ItemsSource,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Category" Background="Coral"></ListBox>
root is the name of the User Control and ItemsSource is the dependency Property.
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(LineBarChart));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set {
SetValue(ItemsSourceProperty, value); }
}
Now, In MainWindow.xaml,
I created an instance of the User Control and Binded to the ItemsSource of Usercontrol this way..
ItemsSource="{Binding ElementName=Window,Path=LineBarData,UpdateSourceTrigger=PropertyChanged}"
where windows is the name of the Main Window
The code behind of the main window is:
public partial class MainWindow : Window
{
private ObservableCollection<LineBarChartData> lineBarData;
internal ObservableCollection<LineBarChartData> LineBarData
{
get
{
return lineBarData;
}
set
{
lineBarData = value;
}
}
public MainWindow()
{
InitializeComponent();
LineBarData = new ObservableCollection<LineBarChartData>();
LineBarData.Add(new LineBarChartData() { Category = "January", ValueX = 10, ValueY = 50 });
LineBarData.Add(new LineBarChartData() { Category = "February", ValueX = 20, ValueY = 60 });
LineBarData.Add(new LineBarChartData() { Category = "March", ValueX = 30, ValueY = 70 });
LineBarData.Add(new LineBarChartData() { Category = "April", ValueX = 40, ValueY = 80 });
}
And the LineBarChartData class is like below
public class LineBarChartData : INotifyPropertyChanged
{
private string _category;
public string Category
{
get { return this._category; }
set { this._category = value; this.OnPropertyChanged("Category"); }
}
private double _valueX;
public double ValueX
{
get { return this._valueX; }
set { this._valueX = value; this.OnPropertyChanged("ValueX"); }
}
private double _valueY;
public double ValueY
{
get { return this._valueY; }
set { this._valueY= value; this.OnPropertyChanged("ValueY"); }
}
//private Brush _color;
//public Brush Color
//{
// get { return this._color; }
// set { this._color = value; this.OnPropertyChanged("Color"); }
//}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Nothing is being displayed in the listbox. I am unable to find my mistake. Please help!
Thanks.
WPF data binding only works with public properties, so
internal ObservableCollection<LineBarChartData> LineBarData { get; set; }
should be
public ObservableCollection<LineBarChartData> LineBarData { get; set; }
You might also drop the backing field and write the property (as read only) like this:
public ObservableCollection<LineBarChartData> LineBarData { get; }
= new ObservableCollection<LineBarChartData>();
Then add values like this:
public MainWindow()
{
InitializeComponent();
LineBarData.Add(new LineBarChartData() { ... });
...
}
As a note, setting UpdateSourceTrigger=PropertyChanged on a Binding only has an effect when it is a TwoWay or OneWayToSource binding, and the Binding actually updates the source property. This is not the case in your Bindings.

Winforms: re-binding when the ViewModel changes

I'm working on a Winforms ReactiveUI app and I have a UserControl that implements IViewFor:
public partial class CustomView : UserControl, IViewFor<CustomViewModel>
{
public CustomViewModel ViewModel { get; set; }
object IViewFor.ViewModel {
get { return ViewModel; }
set { ViewModel = value as CustomViewModel; }
}
public CustomView()
{
InitializeComponent();
this.Bind(ViewModel, x => x.SomeBindingList, x => x.DataGridBindingSource.DataSource);
}
}
In the calling control, I set the ViewModel with:
customView.ViewModel = new CustomViewModel(model)
However, when data changes, customView.ViewModel is re-assigned (using the same code above) but it does not automatically re-bind. I'm assuming that's because ViewModel has no PropertyChanged event.
I could implement INotifyPropertyChanged on CustomView, but I was wondering - is there a convenience method/ReactiveUI way of doing this?
I think you are on the right track with passing a new model instead of replacing the ViewModel. I wasn't sure of your exact requirements, but here is an example that might help. Selecting a new user changes the list of contacts in the CustomView's DataGridView.
Item
public class Item
{
public string Name { get; set; }
public int Value { get; set; }
public Item(string name, int value)
{
Name = name; Value = value;
}
}
MainViewModel
public class MainViewModel : ReactiveObject
{
int _userId;
public int UserId
{
get { return _userId; }
set { this.RaiseAndSetIfChanged(ref _userId, value); }
}
ObservableAsPropertyHelper<User> _user;
public User User => _user.Value;
ObservableAsPropertyHelper<List<Item>> _userList;
public List<Item> UserList => _userList.Value;
public ReactiveCommand<User> LoadUser { get; protected set; }
public ReactiveCommand<List<Item>> LoadUserList { get; protected set; }
public MainViewModel()
{
LoadUser = ReactiveCommand.CreateAsyncObservable(_ => LoadUserImp(UserId));
_user = LoadUser.ToProperty(this, x => x.User, null);
LoadUserList = ReactiveCommand.CreateAsyncObservable(_ => LoadUserListImp());
_userList = LoadUserList.ToProperty(this, x => x.UserList, new List<Item>());
// Listens for change to UserId and loads new User.
this.WhenAnyValue(x => x.UserId).Where(id => id > 0).InvokeCommand(LoadUser);
}
private IObservable<User> LoadUserImp(int userId)
{
User user;
if (userId == 1)
{
user = new User { Id = 1, Name = "Bob" };
}
else
{
user = new User { Id = 2, Name = "Jane" };
}
return Observable.Return(user);
}
private IObservable<List<Item>> LoadUserListImp()
{
Item item1 = new Item("Bob", 1);
Item item2 = new Item("Jane", 2);
List<Item> items = new List<Item> { item1, item2 };
return Observable.Return(items);
}
}
MainView
public partial class MainView : Form, IViewFor<MainViewModel>
{
public MainViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = value as MainViewModel; }
}
public MainView()
{
InitializeComponent();
List<Item> items = new List<Item>();
UserComboBox.DataSource = items;
UserComboBox.DisplayMember = "Name";
UserComboBox.ValueMember = "Value";
// Two way binding.
this.Bind(ViewModel, vm => vm.UserId, v => v.UserComboBox.SelectedValue);
// One way binding.
this.OneWayBind(ViewModel, vm => vm.UserList, v => v.UserComboBox.DataSource);
// Per Paul Betts: Invoking this in the VM constructor means that your VM class becomes more difficult to test,
// because you always have to mock out the effects of calling [LoadUserList],
// even if the thing you are testing is unrelated.
// Instead, I always call these commands in the View.
this.WhenAnyValue(v => v.ViewModel.LoadUserList)
.SelectMany(x => x.ExecuteAsync())
.Subscribe();
// This is where I would change your model, in this case the user in the CustomView.
this.WhenAnyObservable(v => v.ViewModel.LoadUser)
.Subscribe(user => customView1.ViewModel.User = user);
ViewModel = new MainViewModel();
customView1.ViewModel = new CustomViewModel();
}
}
CustomViewModel
public class CustomViewModel : ReactiveObject
{
ObservableAsPropertyHelper<List<Person>> _contacts;
public List<Person> Contacts => _contacts.Value;
User _user;
public User User
{
get { return _user; }
set { this.RaiseAndSetIfChanged(ref _user, value); }
}
public ReactiveCommand<List<Person>> LoadContacts { get; protected set; }
public CustomViewModel()
{
LoadContacts = ReactiveCommand.CreateAsyncObservable(_ => LoadContactsImp(User.Id));
_contacts = LoadContacts.ToProperty(this, x => x.Contacts, new List<Person>());
this.WhenAnyValue(vm => vm.User.Id).InvokeCommand(LoadContacts);
}
private IObservable<List<Person>> LoadContactsImp(int userId)
{
List<Person> contacts;
if (userId == 1)
{
contacts = new List<Person>()
{
new Person() { Id = 1, FirstName = "John", LastName = "Jones" },
new Person() { Id = 2, FirstName = "Beth", LastName = "Johnson" },
};
}
else
{
contacts = new List<Person>()
{
new Person() { Id = 1, FirstName = "Dave", LastName = "Smith" },
new Person() { Id = 2, FirstName = "Elizabeth", LastName = "Bretfield" },
};
}
return Observable.Return(contacts);
}
}
CustomView
public partial class CustomView : UserControl, IViewFor<CustomViewModel>
{
public CustomViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = value as CustomViewModel; }
}
public CustomView()
{
InitializeComponent();
this.OneWayBind(ViewModel, vm => vm.Contacts, v => v.Contacts.DataSource);
}
}
Why the CustomView constructor is called "BridgeGeometryView"?
Are you using ReactiveList or ReactiveBindingList for SomeBindingList in the ViewModel?
Also, I recommend to not set the binding directly, use WhenActivated after InitializeComponent() method:
public BridgeGeometryView()
{
InitializeComponent();
this.WhenActivated(() =>
{
this.Bind(ViewModel, x => x.SomeBindingList, x => x.DataGridBindingSource.DataSource);
});
}

MVVM refresh model

I have a project in WPF with the MVVM-Pattern.
The view has a listview of Persons(FirstName, LastName, Town) and next to the list are the details of the person(FirstName,LastName,ZIP) with a button(Save).
If you click to a person on the left side, the person details on the right side will load automatically.
In the ViewModel I have an ObservableCollection of Person-Models. (Person list)
The Person Model includes some propertys like FirstName, LastName, Town, Zip. (Details).
When I change the ZIP, the Town will updated automatically(in the setter switch-case) e.g. ZIP '11111' Town 'Town1' / ZIP '22222' Town 'Town2'.
The Person Model includes also an ICommand "SavePerson" to Save Changes.
Now when i click to an item in the listview the details will load automatically.
When I change the FirstName or LastName and click "Save" the listview will change the First and the LastName of the selected item, that's ok.
Now when I change the ZIP from '12345' into '11111' the town in the listview is still the old one and not 'Town1'.
Have you an idea to fix this problem, without to implement the INotifyPropertyChanged-Interface in the Model?
Some code:
Model:
public class Person
{
private string _firstName = string.Empty;
private string _lastName = string.Empty;
private string _zip = string.Empty;
private string _town = string.Empty;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName= value; }
}
public string ZIP
{
get { return _zip; }
set
{
switch (value)
{
case "11111":
this.Town = "Town1";
break;
case "22222":
this.Ort = "Town2";
break;
default:
break;
}
_zip = value;
}
}
public string Town
{
get { return _town; }
set { _town= value; }
}
public ICommand SaveNameCommand
{
get
{
return new DelegateCommand((param) => this.SaveName(param));
}
}
private void SaveName(object parameter)
{
string[] param = ((string)parameter).Split(new char[] { ':' });
FirstName = param[0];
LastName = param[1];
PLZ = param[2];
}
}
ViewModel:
public class PersonList
{
private readonly ObservableCollection<Person> _persons = new ObservableCollection<Person>();
private Person _currentSelectedPerson = new Person();
public PersonList()
{
this._persons.Add(new Person() { FirstName = "First1", LastName = "Last1", Ort = "Ort1", PLZ = "112" });
this._persons.Add(new Person() { FirstName = "First2", LastName = "Last2", Ort = "Ort2", PLZ = "122" });
this._persons.Add(new Person() { FirstName = "First3", LastName = "Last3", Ort = "Ort3", PLZ = "1132" });
}
public IEnumerable<Person> Persons
{
get { return this._persons; }
}
}
Since your Model should be loosely-coupled from your ViewModel, it makes sense that you might not want to implement INotifyPropertyChanged in your Model since it's most commonly associated with WPF; however, it's a C# interface and can be used in any type of application, so there's no harm implementing it. In fact, I'd suggest it's probably the best way. However, if you really don't want to implement it in your model classes, consider an event-subscriber model, where the Model raises an event when changed, and the ViewModel subscribes to it.
model.ValuesChanged += model_ValuesChanged;
private void model_ValuesChanged(object sender, EventArgs e)
{
RaisePropertyChanged("MyProperty");
}
I have absolutely no idea why you would want to not implement the INotifyPropertyChanged interface on your model class(es). Really, your only other option would be to implement it in your view model and expose all of your model properties there:
public string FirstName
{
get { return _currentSelectedPerson .FirstName; }
set { _currentSelectedPerson .FirstName = value; NotifyPropertyChanged("FirstName"); }
}
public string LastName
{
get { return _currentSelectedPerson .LastName; }
set { _currentSelectedPerson .LastName= value; NotifyPropertyChanged("LastName"); }
}
...
WPF and the INotifyPropertyChanged interface go hand in hand... at some stage, you're going to have to implement it.
The model Should implement INotifyPropertyChanged
chack INotifyPropertyChanged WPF
any other search of INotifyPropertyChanged will do as well
You need notification your View about changed in ViewModel.
For this use INotifyPropertyChanged. Any class that implements this interface,
notifies any listeners when a property has changed.
So you need to modify our Person class a little bit more:
public class Person : INotifyPropertyChanged
{
private string _firstName = string.Empty;
private string _lastName = string.Empty;
private string _zip = string.Empty;
private string _town = string.Empty;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName= value;
RaisePropertyChanged("LastName");
}
}
public string ZIP
{
get { return _zip; }
set
{
switch (value)
{
case "11111":
this.Town = "Town1";
break;
case "22222":
this.Town = "Town2";
break;
default:
break;
}
_zip = value;
RaisePropertyChanged("LastName");
RaisePropertyChanged("Town");
}
}
public string Town
{
get { return _town; }
set
{
_town= value;
RaisePropertyChanged("Town");
}
}
public ICommand SaveNameCommand
{
get
{
return new DelegateCommand((param) => this.SaveName(param));
}
}
private void SaveName(object parameter)
{
string[] param = ((string)parameter).Split(new char[] { ':' });
FirstName = param[0];
LastName = param[1];
PLZ = param[2];
}
}

property names are different from original Object in the silverlight

Following is part of service layer which is provided by WCF service :
[Serializable]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
public class Service1 : IService1
{
public List<WaitInfo> GetWaitingList()
{
MyDBDataContext db = new MyDBDataContext();
var query = from w in db.WAIT_INFOs
select new WaitInfo
{
TagNo = w.PATIENT_INFO.TAG_NO,
RoomName= w.ROOM_INFO.ROOM_NAME,
PName= w.PATIENT_INFO.P_NAME
};
List<WaitInfo> result = query.ToList();
return result;
}
And following is codebehind part of UI layer which is provided by Silverlight
public MainPage()
{
InitializeComponent();
Service1Client s = new Service1Client();
s.GetWaitingListCompleted +=
new EventHandler<GetWaitingListByCompletedEventArgs>( s_GetWaitingListCompleted);
s.GetWaitingListAsync();
}
void s_GetWaitingListCompleted(object sender,
RadControlsSilverlightApplication1.ServiceReference2.GetWaitingListByCompletedEventArgs e)
{
GridDataGrid.ItemsSource = e.Result;
}
And following is xaml code in Silverlight page
<Grid x:Name="LayoutRoot">
<data:DataGrid x:Name="GridDataGrid"></data:DataGrid>
</Grid>
It is very simple code, however what I am thinking weird is property name of object at "e.Result" in the code behind page.
In the service layer, although properties' names are surely "RoomName, PName, TagNo", in the silverlight properties' names are "roomName, pName, tagNo" which are private variable name of the WaitingList Object.
Did I something wrong?
Thanks in advance.
Unless you specifically decorate your class with the DataContract attribute (which you should, instead of Serializable) then a default DataContract will be inferred. For normal Serializable types, this means the fields will be serialized as opposed to the properties.
You can markup your class in either of the following two ways. The latter will use the property accessors when serializing/deserializing your object which may be very useful or be a hassle depending on your circumstances.
[DataContract]
public class WaitInfo
{
[DataMember(Name="RoomName")]
private string roomName;
[DataMember(Name="PName")]
private string pName;
[DataMember(Name="TagNo")]
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
The method I prefer:
[DataContract]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
[DataMember]
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
[DataMember]
public string PName
{ get { return pName; } set { this.pName = value; } }
[DataMember]
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}

Resources