Just curious: why WPF binding to control's properties not work? - c#

I'm familiar with WPF binding and MVVM. One day when answering questions on Baidu Zhidao I encountered such case:
<Grid>
<ListBox Name="lb" DisplayMemberPath="S"/>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.lb.ItemsSource = new ObservableCollection<Item>()
{
new Item("aa"),
new Item("bb"),
new Item("cc"),
};
}
}
public class Item : ListBoxItem //
{
public Item(string s)
{
this.S = s;
}
public string S { get; set; }
}
If Item inherits ListBoxItem or other control, then nothing displays.
But why? Isn't S always a property of an object?

When you bind ItemsSource to data it creates a ListBoxItem for each element and displays it using the DisplayMemberPath you've specified. However if you bind to an array of ListBoxItem that you've created yourself in code-behind then it will use those instead. In the code you posted your Item class inherits ListBoxItem so it's a GUI element, not data. This is poor practice, but if it's what you actually intend to do then you don't need the S property, just set Content directly:
public class Item : ListBoxItem
{
public Item(string s)
{
this.Content = s;
}
}
But if you want to do it properly (e.g. MVVM) then don't inherit ListBoxItem and don't manipulate the UI elements directly in code-behind:
public class Item
{
public Item(string s)
{
this.S = s;
}
public string S { get; set; }
}

Related

How to Set a Collection of Custom Controls from the property GridView in c#

I have an ObservableCollection of MySearchFields, on a custom Form, lets say CustomForm I declare it as below:
private ObservableCollection<MySearchFields> _mySearchFields = new ObservableCollection<MySearchFields>();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Category("MyFrameWork")]
public ObservableCollection<MySearchFields> MySearchFields
{
get => _mySearchFields;
}
and then on my Main form where it inherits from CustomForm,
when I'm on the designer view and go to propertygrid window I select my ControlType To be ComboBox and then I select DataSource, ValueMember And Display Member. the issue is it does not set DataSource,ValueMember or DisplayMember.
why does this happen? am I missing something?
public class MySearchFields
{
public enum MyControlTypes
{
MyTextBox,
MyDateTimePicker,
MyLbComboBox
}
private MyControlTypes _myControlType = MyControlTypes.MyTextBox;
public MyControlTypes MyControlType
{
get => _myControlType;
set
{
_myControlType = value;
switch (_myControlType)
{
case MyControlTypes.MyTextBox:
{
MyControl = new TextBox();
break;
}
case MyControlTypes.MyLbComboBox:
{
MyControl = new MyLbComboBox();
break;
}
case MyControlTypes.MyDateTimePicker:
{
MyControl = new DateTimePicker();
break;
}
}
}
}
public Control MyControl { get; set; }
public MySearchFields()
{
MyControl = new Control();
}
}
Your error is that on your case switcher you insert new MyLBcombobox instead of just ComboBox.
try initializing public Control MyControl { get; set; } with the proper setters and getters, as well as a private variable the backs it up,
Furthermore just use the fullprop snippet of Visual studio
just write "fullprop" and the press 2 times TAB, swapping around => and {} is not that nice.

TreeView with ObservableCollection Source, no update

I create a simple Treeview that I bound to an ObservableCollection.
ObservableCollection<IMarketDataViewModel> MarketDataItems;
public interface IMarketDataViewModel
{
string Title { get; }
ObservableCollection<IMarketDataViewModel> Items { get; set; }
}
public MarketDataUserControl(IMarketDataViewer viewModel)
{
InitializeComponent();
DataContext = viewModel;
marketDataTreeView.ItemsSource = viewModel.MarketDataItems;
}
When I update data in my ViewModel, I only see the first level in my Treeview. The only way I found to resolve the problem is to create an event in my ViewModel and when the data is updated instead calling PropertyChange on MarketDataItems, I trigger the event and the View reset marketDataTreeView.ItemsSource like this :
private void ViewModelOnOnUpdateItems()
{
marketDataTreeView.ItemsSource = null;
marketDataTreeView.ItemsSource = viewModel.MarketDataItems;
}
And this work perfectly --> All levels are displayed.
Someone know why the PropertyChange doesn't work and why I have to reset the ItemsSource ?
I think you should implement a binding to the ItemSource and this is done by a property:
// Create property
public ObservableCollection<IMarketDataViewModel> MarketDataItems { get; private set; }
...
// Create Binding
Binding bindingObject = new Binding("MarketDataItems");
bindingObject.Source = this; //codebehind class instance which has MarketDataItems
marketDataTreeView.SetBinding(TreeView.ItemsSource, bindingObject);
Or the binding in XAML:
<TreeView x:Name="marketDataTreeView" ItemsSource="{Binding Path=MarketDataItems}"/>
Finally the issue is that I didn't call OnPropertyChanged("Items")
public class MarketDataViewModelBase : IMarketDataViewModel, INotifyPropertyChanged
{
.....
private ObservableCollection<IMarketDataViewModel> items;
public ObservableCollection<IMarketDataViewModel> Items
{
get { return items; }
set
{
items = value;
OnPropertyChanged("Items"); //Add this line fix my issue
}
}
}

Getting value from SelectedItem to other method

So I have RadGridView with ContextMenu that has ItemClick event with this code :
ListData selectedItem = new ListData();
public void GridContextMenu_ItemClick(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
MenuItem item = (e.OriginalSource as RadMenuItem).DataContext as MenuItem;
switch (item.Text)
{
case "Edit":
selectedItem = (GridView.SelectedItem);
editMenu.Show();
this.Close();
break;
case "Delete":
this.GridView.Items.Remove(this.GridView.SelectedItem);
break;
}
}
The ListData is something like this :
public class ListData
{
public string Name { get; set; }
public string Type { get; set; }
...
public string Something { get; set; }
}
I know that selectedItem inside case"Edit" has one row data from Name, Type, ... , Something from the clicked row when i click the "Edit" Menu. But how can i use the selectedItem in other method in the same .xaml.cs or even in other file?
OK, I'm not sure if this is exactly what you need, but here is what worked for me.
At the top of the class I have this :public ListData selectedItem;
Inside the class constructor I have this: selectedItem = new ListData();
and also this public ObservableCollection<ListData> OCLData;
I created a datagrid named dgMirza, and set the ItemsSource = OCLData. OCLData is a collection of ListData objects.
Inside the SelectedCellsChanged event I put this: selectedItem = (ListData)dgMirza.SelectedItem;
I was able to see data in the selectedItem object even in other methods.

How to add TreeViewItems to a TreeView Control

This question is a follow up to this question. I've taken some advice from this site and decided to start learning MVVM implementation for my work with TreeViews. With that being said I am VERY new to MVVM and I'm still getting familiar with the syntax and implementation.
I have a TreeView that displays integer-type data but I would like it to work with strings instead. The tree also allows the user to add to any level by selecting the TreeViewItem and then typing in the new integer header into the textBox, and then clicking a button.
I'd like for a parent, child, and grandchild to be available at start-up with pre-defined names. Another thing to note is that that user will only be able to add to the TreeView at the level of the child (so they'll only be able to add grandchildren).
Model
public class TreeViewModel : PropertyChangedBase
{
public string Value { get; set; }
public ObservableCollection<TreeViewModel> Items { get; set; }
public CollectionView ItemsView { get; set; }
public TreeViewModel(string value)
{
Items = new ObservableCollection<TreeViewModel>();
ItemsView = new ListCollectionView(Items)
{
SortDescriptions =
{
new SortDescription("Value",ListSortDirection.Ascending)
}
};
Value = value;
}
}
ViewModel
public class SortedTreeViewWindowViewModel : PropertyChangedBase
{
private string _newValueString;
public string NewValueString
{
get { return _newValueString; }
set
{
_newValueString = value;
OnPropertyChanged("NewValueString");
}
}
public TreeViewModel SelectedItem { get; set; }
public ObservableCollection<TreeViewModel> Items { get; set; }
public ICollectionView ItemsView { get; set; }
public SortedTreeViewWindowViewModel()
{
Items = new ObservableCollection<TreeViewModel>();
ItemsView = new ListCollectionView(Items) { SortDescriptions = { new SortDescription("Value", ListSortDirection.Ascending) } };
}
public void AddNewItem()
{
ObservableCollection<TreeViewModel> targetcollection;
//Insert the New Node as a Root node if nothing is selected.
targetcollection = SelectedItem == null ? Items : SelectedItem.Items;
if (_newValueString != null)
{
targetcollection.Add(new TreeViewModel(_newValueString));
NewValueString = string.Empty;
}
}
}
View Code-Behind
public partial class Window1 : Window
{
public SortedTreeViewWindowViewModel ViewModel { get { return DataContext as SortedTreeViewWindowViewModel; } set { DataContext = value; } }
public Window1()
{
InitializeComponent();
ViewModel = new SortedTreeViewWindowViewModel()
{
Items = {new TreeViewModel("Test")}
};
}
private void AddNewItem(object sender, RoutedEventArgs e)
{
ViewModel.AddNewItem();
}
private void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
ViewModel.SelectedItem = e.NewValue as TreeViewModel;
}
}
Thanks so much for the help. I'm hoping that going through this will help me understand how to edit and build ViewModels so that I can learn to improvise a little more in the future.
UPDATE
The TreeView is now composed of strings, so that part is solved. I still need help with the default nodes though. I have updated my code to reflect this change.
Here are my suggestions
Change the places where you have int to string. The TreeView should handle that change.
In the constructor of your ViewModel, manually insert your default nodes. Make sure you understand how to work with a TreeView as that will affect the design of your Model and ViewModel and naturally improve the implementation.
Here is a very simple example how to fill a tree in the ViewModel which is bound to a TreeView in the View:
View
<Window x:Class="TreeViewExample.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">
<StackPanel>
<StackPanel>
<TreeView ItemsSource="{Binding Tree}"/>
</StackPanel>
</StackPanel>
</Window>
View Code-Behind
namespace TreeViewExample
{
using System.Windows;
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new MainWindowViewModel();
InitializeComponent();
}
}
}
ViewModel
namespace TreeViewExample
{
using System.Collections.ObjectModel;
using System.Windows.Controls;
class MainWindowViewModel
{
public ObservableCollection<TreeViewItem> Tree { get; set; }
public MainWindowViewModel()
{
Tree = new ObservableCollection<TreeViewItem>();
Tree.Add(GetLoadedTreeRoot());
}
private TreeViewItem GetLoadedTreeRoot()
{
TreeViewItem parent = new TreeViewItem() { Header = "Parent" };
TreeViewItem child1 = new TreeViewItem() { Header = "Child 1" };
TreeViewItem child2 = new TreeViewItem() { Header = "Child 2" };
TreeViewItem grandchild1 = new TreeViewItem() { Header = "Grandchild 1" };
TreeViewItem grandchild2 = new TreeViewItem() { Header = "Grandchild 2" };
child1.Items.Add(grandchild1);
child2.Items.Add(grandchild2);
parent.Items.Add(child1);
parent.Items.Add(child2);
return parent;
}
}
}
Produces:
Parent
Child 1
Grandchild 1
Child 2
Grandchild 2
Additional thoughts:
To clean up your code-behind, you might look up a Command implementation, of which there are many. Although you sometimes need it, avoid code in the code-behind when possible. I really like this example because it shows you a general MVVM implementation without getting into advanced Command-related topics (ItemTemplates, Interactivity namespace, etc.).

How do you bind ObservableCollections to ItemsSource?

DataContextDataContext context1 = new DataContextDataContext();
public MainWindow()
{
InitializeComponent();
DataContext = new ObservableCollection<MyObject>();
RadGridView1.Filtered+=new EventHandler<GridViewFilteredEventArgs>(RadGridView1_Filtered);
ObservableCollection<MyObject> _MyObject = new ObservableCollection<MyObject>();
foreach (var p in context1.Students)
{
_MyObject.Add(new MyObject { ID = p.StudentID, Name = p.StudentFN });
}
}
void RadGridView1_Filtered(object sender, GridViewFilteredEventArgs e)
{
RadGridView1.ItemsSource = ObservableCollection<MyObject>();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
public class MyObject
{
public int ID { get; set; }
public string Name { get; set; }
}
How do you bind my ObservableCollections to the ItemsSource?
You want to set the ItemSource to the instance of an ObservableCollection you created in the constructor:
RadGridView1.ItemsSource = _MyObject;
You can make the observable collection as a public property in your code-behind/presenter/viewmodel, like
public ObservableCollection<MyObject> MyObjectCollection {get;set;}
then you can populate that and the binding can be code code behind.
ItemsSource is a dependency property you can bind it in XAML or code behind, like suppose you want to bind to ListBox's(say named lstItems) ItemsSource, like (below code is considering that 'MyObjectCollection' is in codebehind
Binding bindingObject = new Binding("MyObjectCollection");
bindingObject.Source = this; //codebehind class instance which has MyObjectCollection
lstItems.SetBinding(ListBox.ItemsSource, bindingObject);
or in XAML,
<ListBox x:Name="lstItems" ItemsSource="{Binding Path=MyObjectCollection}"/>
for both the ways above you need to set the datacontext which is 'this' (for this specific solution).
But maybe you want to look into basic WPF databinding where you can understand Depedency properties, binding objects, binding modes, etc.
http://msdn.microsoft.com/en-us/library/aa480224.aspx
http://msdn.microsoft.com/en-us/library/ms750612.aspx
http://joshsmithonwpf.wordpress.com/2008/05/19/gradual-introduction-to-wpf-data-binding/

Categories

Resources