How to bind property to a TreeView - wpf

I want to create a relationship between my treeview and datagrid
I have a method called SelectedTestElement that calls a the SearchGrid method
ViewModel:
public TestElementViewModel SelectedTestElement
{
set
{
if (_selectedTestElement == value) return;
this._ selectedTestElement = value;
SearchGrid(value.Id, new List< TestElementViewModel>( testElementViewModel.GetAllTreeNodes()));
}
get
{
return this._ selectedTestElement;
}
}
I want to bind this property in my xaml TreeView
<TreeView Margin="644,137,6,6" Grid.RowSpan="2" ItemsSource="{Binding MessageElements, Mode=TwoWay}" TreeViewItem.Selected="{Binding SelectedTestElement, Mode=TwoWay}" />
Is treeView.Selected a best way to make the property call in treeview?
Edit:
ViewModel:
I select a treenode i.e SelectedTreeNode will be bind to TreeView.SelectedItem
public TreeNodeViewModel SelectedTreeNode
{
set
{
if (_selectedTreeNode== value) return;
this._selectedTreeNode = value;
//search the treenode id
SearchGrid(value.Id, TestFieldVModel.GetAllTestField());
}
get
{
return this._selectedTreeNode;
}
}
//do the search and highlight the matching id grid
public void SearchGrid(int id, ObservableCollection<TestFieldViewModel> msgField)
{
foreach (var c in msgField)
{
c.DisplayColor = c.Id == id ? Brushes.DarkOrange : Brushes.Black;
c.DisplayFont = c.Id == id ? FontWeights.Bold : FontWeights.Normal;
// SearchGrid(id, c.GetAllTestField());
}
}
XAML:
<TreeView Margin="644,137,6,6" Grid.RowSpan="2" ItemsSource="{Binding TreeNodes, Mode=TwoWay}" TreeView.SelectedItem ="{Binding Path= SelectedTreeNode, Mode=TwoWay}" >

This won't work, you're trying to bind an event to a property on your view model.
I would recommend that you use the TreeView.SelectedItem property and bind that to a new property on your view model that accepts a TreeViewItem. Then either modify your SelectedTestElement property to look at the new selected item property and determine the appropriate TestElementViewModel to return, or you could have the set of the new selected item property set the `this._selectedTestElement' directly.

Related

Binding ObservableDictionary to ComboBox (WPF)

I would have thought this question would have been answered already, but I am not finding it. I have a simple class:
public class BinanceEndpoint : ObservableObject
{
private string baseAddress;
private string socketBaseAddress;
public string BaseAddress
{
get { return baseAddress; }
set { baseAddress = value; RaisePropertyChangedEvent(nameof(BaseAddress)); }
}
public string SocketBaseAddress
{
get { return socketBaseAddress; }
set { socketBaseAddress = value; RaisePropertyChangedEvent(nameof(SocketBaseAddress)); }
}
}
I am then populating an ObservableDictionary with objects from that class:
private MainViewModel()
{
private BinanceEndpoint apiEndPoint;
private ObservableDictionary<string, BinanceEndpoint> endPoints = new ObservableDictionary<string, BinanceEndpoint>();
endPoints.Add("Binance.com", new BinanceEndpoint() { BaseAddress = "https://api.binance.com", SocketBaseAddress = "wss://stream.binance.com:9443", });
endPoints.Add("Binance.us", new BinanceEndpoint() { BaseAddress =
"https://api.binance.us", SocketBaseAddress = "wss://stream.binance.us:9443", });
endPoints.Add("Testnet", new BinanceEndpoint() { BaseAddress = "https://testnet.binance.vision", SocketBaseAddress = "wss://testnet.binance.vision", });
}
[JsonIgnore]
public ObservableDictionary<string,BinanceEndpoint> EndPoints
{
get { return endPoints; }
set { endPoints = value; RaisePropertyChangedEvent(nameof(EndPoints)); }
}
public BinanceEndpoint APIEndPoint
{
get { return apiEndPoint; }
set { apiEndPoint = value; RaisePropertyChangedEvent(nameof(APIEndPoint)); }
}
}
Then I am trying to populate a ComboBox from using the ObservableDictionary.
<ComboBox Grid.Column="1" ItemsSource="{Binding EndPoints}" DisplayMemberPath="Key" SelectedValuePath="Value" SelectedValue="{Binding Path=APIEndPoint, Mode=TwoWay}"/>
The issue I am having is that the SelectedValue does not update the value of the ComboBox when it is loaded. What am I doing wrong?
You should avoid binding to a Dictionary in the first place. There is a reason that there is no ObservableDictionary in the .NET library - since the year of release in 2002. I guess we agree that this is not because MS developers didn't know how to implement such a dictionary after all these years.
Your are using SelectedValuePath and SelectedValue wrong. SelectedValue returns the value of the property that SelectedValuePath is pointing to. SelectedValuePath provides a property path on the SelectedItem.
When you actively set the SelectedValue, then the control will try to find and select the item where the item's property specified by SelectedValuePath matches the value of SelectedValue.
When SelectedItem returns the selected item (instance), then SelectedValue returns the value of a property on this SelectedItem that is specified using SelectedValuePath.
For example: we bind to a Dictionary<string, BinanceEndpoint>. To display the Key of your Dictionary we specify the DisplayMemberPath (as an alternative to a DataTemplate).
The SelectedItem will hold KeyValuePair<string, BinanceEndpoint>.
To have the ComboBox.SelectedValue return the selected item's SocketBaseAddress value, we must set the SelectedValuePath to "Value.SocketBaseAddress":
<ComboBox x:Name="ComboBox"
ItemsSource="{Binding EndPoints}"
DisplayMemberPath="Key"
SelectedValuePath="Value.SocketBaseAddress" />
<!-- Display the selected item's SocketBaseAddress value -->
<TextBlock text="{Binding ElementName=ComboBox, Path=SelectedValue}" />
If you want the SelectedValue to return the BinanceEndpoint instance then set SelectedValuePath to "Value":
<ComboBox x:Name="ComboBox"
ItemsSource="{Binding EndPoints}"
DisplayMemberPath="Key"
SelectedValuePath="Value" />
<!-- Display the selected item's 'SocketBaseAddress' value -->
<TextBlock text="{Binding ElementName=ComboBox, Path=SelectedValue.SocketBaseAddress}" />

Listing and binding to properties of a type, not a collection of types

I have a Listbox with property names of type Color from a MySettings object. I want to create another control (let's say a TextBox, the type is irrelevant) that binds to the value of property name listed.
I am at a loss as to how to achieve that. As it is now, the listbox is just a list of strings, obviously, it has no knowledge of the MySettings object.
One of the things I thought about is creating a collection of objects of a custom type that somehow binds to the MySettings object, and setting that as ItemsSource for the ListBox.
I know I trying to reinvent the wheel here, so I would appreciate any pointers to get me started.
Code-behind, ListBox is currently defined in xaml with no properties other than x:Name="listBox"
foreach (var prop in settings.GetType().GetProperties()
.Where(p => p.PropertyType == typeof(System.Drawing.Color))
.OrderBy(p => p.Name))
{
listBox.Items.Add(new ListBoxItem()
{
Content = prop.Name,
});
}
MySettings contains several properties:
[UserScopedSetting()]
[ConfigurationProperty(nameof(BackColor), DefaultValue = "GhostWhite")]
public System.Drawing.Color BackColor
{
get { return (System.Drawing.Color)base[nameof(BackColor)]; }
set
{
base[nameof(BackColor)] = value;
OnPropertyChanged();
}
}
[UserScopedSetting()]
[ConfigurationProperty(nameof(ForeColor), DefaultValue = "Black")]
public System.Drawing.Color ForeColor
{
get { return (System.Drawing.Color)base[nameof(ForeColor)]; }
set
{
base[nameof(ForeColor)] = value;
OnPropertyChanged();
}
}
You could assign the ListBox's ItemsSource to a collection of name/value pairs (an anonymous class here, but could as well be a regular class):
listBox.ItemsSource = settings.Properties
.OfType<SettingsProperty>()
.Where(p => p.PropertyType == typeof(System.Drawing.Color))
.Select(p => new
{
Name = p.Name,
Value = settings[p.Name]
});
Then declare a ListBox ItemTemplate that shows the Name property, and a TextBlock that binds to the Value property of the selected ListBox item:
<ListBox x:Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Text="{Binding ElementName=listBox, Path=SelectedItem.Value}"/>

WPF binding - DataGrid.Items.Count

in my View, there are a DataGrid and a TextBox, which is bound to the DataGrid's Items.Count property:
<DataGrid x:Name="dataGrid" ItemsSource="{Binding dataTable}"/>
<TextBox Text="{Binding Items.Count,ElementName=dataGrid,Mode=OneWay,StringFormat={}{0:#}}"/>
The ViewModel has a property (e.g. ItemsCount) which I'd like to be bound to the Items.Count property of the DataGrid, but have no idea, how to achieve this.
class ViewModel : INotifyPropertyChanged
{
public DataTable dataTable {get;set;}
public int ItemsCount {get;set;}
}
Maybe I could also use the Rows.Count property of the DataTable the DataGrid is bound to, but how would i bind or link the two properties in the ViewModel?
So I basically want the ItemsCount property to be synchronized with the dataTable.Rows.Count property.
A common way to achieve your requirements are to declare properties to data bind to the UI controls:
<DataGrid x:Name="dataGrid" ItemsSource="{Binding Items}" />
<TextBox Text="{Binding ItemsCount}" />
...
// You need to implement the INotifyPropertyChanged interface properly here
private ObservableCollection<YourDataType> items = new ObservableCollection<YourDataType>();
public ObservableCollection<YourDataType> Items
{
get { return items; }
set { items = value; NotifyPropertyChanged("Items"); NotifyPropertyChanged("ItemCount"); }
}
public string ItemCount
{
get { Items.Count.ToString("{0:#}"); }
}
UPDATE >>>
As #Sivasubramanian has added his own requirement to your question, in case you need to update the item count specifically by adding to your collection, you can manually call the NotifyPropertyChanged method:
Items.Add(new YourDataType());
NotifyPropertyChanged("ItemCount");

How can I add items from a listbox to a list by clicking a button without any codebehind?

I am new to MVVM, and also fairly new to WPF. As a matter of fact I started programming just a few months ago. MVVM is really dng my head in with the binding concept, and I have been trying for days now to just simply make an application that allows you to select an item from a listbx, and when you click on the add button the selected item should be saved in a new list. The second listbox displays the latest items added, and you can select an item and delete it by using another button. ususally I would go for the click event and decorate my codebehind with pretty little methods, but I really want to learn how to do all this by using bindings and no codebehind.
I would be extremly happy for any help, and please remember that I am new to this and I really want to keep it as simple as possible :)
with kind regards Daniela
<WrapPanel HorizontalAlignment="Center" Margin=" 10">
<ListBox x:Name="Firstbox"
Width="100"
ItemsSource="{Binding FoodList}"
DisplayMemberPath="Name" >
</ListBox>
<Button Margin="10 >Select</Button>
<ListBox Width="100"></ListBox>
private List _foodList;
public List<FoodItem> FoodList
{
get { return _foodList; }
set { _foodList = value; }
}
private List<FoodItem> _newFoodList;
public List<FoodItem> NewFoodList
{
get { return _newFoodList; }
set { _newFoodList = value; }
}
public MainViewModel()
{
InitializeCommands();
GetFood();
}
private void GetFood()
{
FoodList = new List<FoodItem>()
{
new FoodItem() {Name="Applepie"},
new FoodItem() {Name="Scones"}
};
}
first, you need to replace the Lists with ObservableCollections, so that the UI can detect when new items are added.
Add a SelectedItem property to your ViewModel:
private FoodItem _selectedItem;
public FoodItem SelectedItem
{
get { return _selectedItem;}
set
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
bind the SelectedItem property of the 1st ListBox to this property:
<ListBox Width=" 100" x:Name="Firstbox"
ItemsSource="{Binding FoodList}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedItem}" />
bind your 2nd ListBox to the NewFoodList property
create a command in your ViewModel:
private DelegateCommand _addItemCommand;
public ICommand AddItemCommand
{
get
{
if (_addItemCommand == null)
{
_addItemCommand = new DelegateCommand(AddItem);
}
return _addItemCommand;
}
}
void AddItem()
{
if (SelectedItem != null)
NewFoodList.Add(SelectedItem);
}
And finally, bind the button's Command property to the AddItemCommand property:
<Button Margin="10" Command="{Binding AddItemCommand}" >Select</Button>

How to set ItemSource and ItemTemplate to display a list of objects

I have a List box that I want to display a list of objects, I am following the MVVM pattern and am finding it difficult to achieve what I want.
MainWindowView.xaml
<ListBox ItemsSource="{Binding Path=MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MainWindowViewModel.cs
private List<ListBoxItem> _myList = new List<ListBoxItem>();
public List<ListBoxItem> MyList
{
get { return _myList ; }
set
{
_myList = value;
OnPropertyChanged("MyList");
}
}
public SprintBacklogViewModel()
{
foreach(MyObject obj in MyObjects.MyObjectList)
{
ListBoxItem item = new ListBoxItem();
item.Content = obj;
MyList.Add(item);
}
}
MyList is getting updated correctly, but nothing is displaying in the window. (ItemsSource="{Binding Path=MyList}" also works, I tested with different data) I have not used an ItemTemplate before so any pointers are welcome. My understanding of it is that If I set it up correctly It will display the data in my objects.
eg:
<Label Content="{Binding Path=Name}"/>
there is a property in MyObject called Name, I want to display this as a label in my list
*EDIT
In my window I get a line of text - mynamespace.MyObject
MyList property in ViewModel is property of type ListBoxItem, it has property Name but it's not Name of MyObject. So you need to change your property in your ViewModel by
Replace
private List<ListBoxItem> _myList = new List<ListBoxItem>();
public List<ListBoxItem> MyList
{
get { return _myList ; }
set
{
_myList = value;
OnPropertyChanged("MyList");
}
}
with
private List<MyObject> _myList = new List<MyObject>();
public List<MyObject> MyList
{
get { return _myList ; }
set
{
_myList = value;
OnPropertyChanged("MyList");
}
}
Your list should not contain UI-Elements but data (you are data-binding), if you bind to a list of ListBoxItems the ListBox will disregard the ItemTemplate and just use the items as they fit the expected container for the ListBox. Containers will be generated automatically, you do not need to do that in your list.
If you add items to a collection at runtime the binding engine needs to be notified to update the changes, for this you should use an ObservableCollection or anything that implements INotifyCollectionChanged. (When doing so you further usually make the field readonly and only provide a getter) This is the reason why there are no items.

Resources