I have a ListBox control in the WPF form which contains the Button control.
Button control has the int values like 65.
I want to display seven buttons which is divided of the 65.
Also I want to put the content on the button which says 1 - 10, 11 - 20, 21 - 30, etc.. and 65 value comes from the database.
You can use ListBox.ItemTemplate to create the listbox which contains the buttons.
Here is the sample which I have created which binds the ObservalbleCollection as the ItemSource of the listbox:
<ListBox ItemsSource="{Binding ListDiv}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The .cs File for the same is:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<string> listDiv;
public ObservableCollection<string> ListDiv
{
get { return listDiv; }
set
{
listDiv = value;
OnNotifyPropertyChanged("ListDiv");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
GetList();
}
private void GetList()
{
ListDiv = new ObservableCollection<string>();
ListDiv.Add("1-10");
ListDiv.Add("2-20");
ListDiv.Add("3-30");
ListDiv.Add("4-40");
ListDiv.Add("5-50");
ListDiv.Add("6-60");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged(string S)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(S));
}
}
You can use your own logic part to Get the Observable Collection in GetList() Method.
Don't Forget to set the DataContext of the Class.
Hope this will help you, Thank you.
Related
I have this problem for some time already. I want to receive some data via MQTT and display these messages in a ListBox. It has worked without MVVM, but not with it.
What happens is that the data gets revieved and passed to the ListBox. When I hove over the ListBox, I can see that there is some data since it shows an item can be displayed.
My code is the following:
View
public partial class ShellView : Window
{
public ShellView()
{
InitializeComponent();
this.DataContext = new ShellViewModel();
}
}
ViewModel
ObservableCollection<string> _data = new ObservableCollection<string>();
public ObservableCollection<string> Data
{
get
{
return _data;
}
set {
_data = value;
RaisePropertyChanged("Data");
}
}
#region INotifyPrortyChanged
public const string propertyName = "Data";
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Part of the ViewModel where I suspect the error
private void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
Application.Current.Dispatcher.Invoke((Action)delegate
{
_data.Add(Encoding.UTF8.GetString(e.Message));
});
foreach (string o in _data.ToList())
{
//MessageBox.Show(o); shows that the string "json" is recieved
Application.Current.Dispatcher.Invoke((Action)delegate
{
//MessageBox.Show(o); same here
Data.Add(o);
});
}
}
View.Xaml
ListBox x:Name="lBox" ItemsSource="{Binding Data}" Background="White" Grid.Column="1" Margin="192,52,134,10" BorderBrush="Black" BorderThickness="2" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Value}" Background="red"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If someone could help me figuring out the problem in my code, it would be deeply appreciated.
ItemsSource has type ObservableCollection<string>, so each element in ListBox is a primitive string.
But inside ItemTemplate you have this binding: {Binding Path=Value}. string doesn't have Value property, so text value cannot be resolved and nothing is displayed.
Change the binding to {Binding Path=.} or simply: {Binding}
Debugging tip: look into Visual Studio Output window - IDE reports incorrect bidnings there with details (like not existing property names, type mismatches and so on)
In two combobox A and B.
A's ItemsSource is Custom list. and B's ItemsSource is UserControl list.
When manually setting the SelectedItem, A combobox works well, but B combobox UI do not show the selected Item. (In debugging, SelectedItem's value mapping is right, but the combobox B's UI do not be changed.)
All the other structure is same between A and B. What is the reason?
MainWindow.xaml
...
<ComboBox ItemsSource="{Binding FruitList}" SelectedItem="{Binding SelectedFruit}"
DisplayMemberPath="FruitName" />
<Button Content="Button" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<ComboBox ItemsSource="{Binding UserControlList}" SelectedItem="{Binding SelectedUserControl}" DisplayMemberPath="ItemName" />
<Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="Button_Click2"/>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
FruitList.Add(f1);
FruitList.Add(f2);
FruitList.Add(f3);
UserControlList.Add(u1);
UserControlList.Add(u2);
UserControlList.Add(u3);
}
Fruit f1 = new Fruit { FruitName = "Apple" };
Fruit f2 = new Fruit { FruitName = "Banana" };
Fruit f3 = new Fruit { FruitName = "Lemon" };
MyUserControl u1 = new MyUserControl { ItemName = "Apple" };
MyUserControl u2 = new MyUserControl { ItemName = "Banana" };
MyUserControl u3 = new MyUserControl { ItemName = "Lemon" };
ObservableCollection<Fruit> _FruitList = new ObservableCollection<Fruit>();
public ObservableCollection<Fruit> FruitList
{
get { return _FruitList; }
set
{
_FruitList = value;
OnPropertyChanged();
}
}
Fruit _SelectedFruit;
public Fruit SelectedFruit
{
get { return _SelectedFruit; }
set
{
_SelectedFruit = value;
OnPropertyChanged();
}
}
ObservableCollection<MyUserControl> _UserControlList = new ObservableCollection<MyUserControl>();
public ObservableCollection<MyUserControl> UserControlList
{
get
{
return _UserControlList;
}
set
{
_UserControlList = value;
OnPropertyChanged();
}
}
MyUserControl _SelectedUserControl;
public MyUserControl SelectedUserControl
{
get { return _SelectedUserControl; }
set
{
_SelectedUserControl = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.SelectedFruit = f3;
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
this.SelectedUserControl = u3;
}
}
public class Fruit
{
public string FruitName { get; set; }
}
}
UserControl
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public string ItemName { get; set; }
}
This is not the good way of achieving this. Better define the ItemTemplate for the combobox to have the UserControl in it like:
<ComboBox ItemsSource="{Binding ItemList}" SelectedItem="{Binding SelectedItem}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<myControls:MyUserControl/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
and define the class Item
public class Item
{
public string ItemName { get; set; }
}
ObservableCollection<Item> _ItemsList = new ObservableCollection<Item>();
public ObservableCollection<Item> ItemsList
{
get
{
return _ItemsList ;
}
set
{
_ItemsList = value;
OnPropertyChanged();
}
}
Here DataContext of your UserControl will be Item object. you can bind the ItemName within you user control to show it in anyway you want.
in your user control you can have:
<TextBlock Text="{Binding ItemName}"></TextBlock>
Since you have asked "What is the reason?":
The reason why the second combo box does not show any selection is that ComboBox handles items of type ContentControl specially. In the read-only selection box, it is not the ContentControl that is used to display the value, but the content of the ContentControl. Since a UserControl is a ContentControl, the content of the UserControl is displayed inside the selection box, and therefore you have lost the data context of the UserControl; in the end, an empty string is displayed even though SelectedItem contains a reference to the UserControl that still has a valid data context. (As far as I know this behavior is undocumented; but you can see that it works like this by examining the ComboBox's code on http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/ComboBox.cs, especially the UpdateSelectionBoxItem() method).
By setting IsEditable="True" on the second ComboBox, you can see that everything works fine if the combo box has no read-only selection box.
Therefore, you generally should avoid adding UI elements to combo boxes, especially if you are using the DisplayMemberPath property, i.e. if you never want to actually display the UI element.
The recommended way to display ComboBox items with non-standard appearance (e.g. with UserControls) is described in the answer of #nit.
If you, however, insist on passing a UserControl item list to the ComboBox, you might remove DisplayMemberPath and use something like this:
<ComboBox ItemsSource="{Binding UserControlList}" SelectedItem="{Binding SelectedUserControl}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Furthermore, in the constructor of your UserControl, you must place this line:
((FrameworkElement) Content).DataContext = this;
This is necessary to make sure that the correct data context is available in the read-only selection box, which only contains the content of the user control, not the user control itself.
Please note, that with the above example, the drop-down list contains text only (i.e. the item names), but the selection box will contain the fully rendered user control.
In my main window I have a class where I store all my data in ( called "Measconsettings"). This class ("Measconsettings") contains an ObservableCollection "DeviceDefinitionList" of an other class "DeviceDefinition"
ObservableCollection<DeviceDefinition> DeviceDefinitionList.
When I press a button in my MainWindow a new window is created with datacontex = Measconsettings.
In this new window there is a combobox which ItemsSource is bound to "Measconsettings.DeviceDefinitionList".
<ComboBox Grid.Row="2" Grid.Column="2" Margin="2,0" Name="CboDeviceDefinitionList" ItemsSource="{Binding Path=DeviceDefinitionList}"/>
My problem now is that the combobox does not update automaticly when an item is added.
I need to close the new window and press the button again to open it and only then it shows the items in the combobox.
I tried adding CboDeviceDefinitionList.Items.Refresh(); but it does not work..
Only when I add CboDeviceDefinitionList.ItemsSource = orgMeasConSettings.DeviceDefinitionList; right after adding items to the ObservableCollection then they show up right away.
Any ideas or tips on how to properly bind to a combobox? PS: I'm wpf beginner
This is my solution after implementing INotifyPropertyChanged as suggested by manish.
EDIT: It now works!
public class MeasConSettings : INotifyPropertyChanged
{
private ObservableCollection<DeviceDefinition> mDeviceDefinitionList;
private DeviceDefinition mSelectedDeviceDefinition;
public ObservableCollection<DeviceDefinition> DeviceDefinitionList
{
get
{
return mDeviceDefinitionList;
}
set
{
mDeviceDefinitionList = value;
}
}
public DeviceDefinition SelectedDeviceDefinition
{
get
{
return mSelectedDeviceDefinition;
}
set
{
mSelectedDeviceDefinition = value;
NotifyPropertyChanged("SelectedDeviceDefinition");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
XAML CODE:
<ComboBox Grid.Row="2" Grid.Column="2" Margin="2,0" Name="CboDeviceDefinitionList" ItemsSource="{Binding Path=DeviceDefinitionList}" SelectedItem="{Binding Path=SelectedDeviceDefinition}"/>
CODE for adding item:
orgMeasConSettings.DeviceDefinitionList.Clear();
foreach (DeviceDefinition deviceDefinition in newSettings.DeviceDefinitionList)
{
orgMeasConSettings.DeviceDefinitionList.Add(deviceDefinition);
}
orgMeasConSettings.SelectedDeviceDefinition = newSettings.DeviceDefinitionList.FirstOrDefault();
I got a sample mvvm app. The UI has a textbox, a button and a combobox. when I enter something in the textbox and hit the button, the text I enter gets added to an observablecollection. The Combobox is bound to that collection. How do I get the combobox to display the newly added string automaticly?
As I understand correctly, you want to add an item and select it.
Here is the example how it can be done using ViewModel and bindings.
Xaml:
<StackPanel>
<TextBox Text="{Binding ItemToAdd}"/>
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" />
<Button Content="Add" Click="Button_Click"/>
</StackPanel>
ViewModel:
public class MainViewModel:INotifyPropertyChanged
{
public ObservableCollection<string> Items { get; set; }
public string ItemToAdd { get; set; }
private string selectedItem;
public string SelectedItem
{
get { return selectedItem; }
set
{
selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
public void AddNewItem()
{
this.Items.Add(this.ItemToAdd);
this.SelectedItem = this.ItemToAdd;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The MainViewModel has 3 properties (one for the TextBox and two other for the ComboBox) and the method AddNewItem without parameters.
The method can be triggered from a command, but there is no standard class for commands, so I will call it from the code-behind:
((MainViewModel)this.DataContext).AddNewItem();
So you must explicitly set an added item as selected after you add it to a collection.
Because the method OnItemsChanged of the ComboBox class is protected and can't be used.
If the ComboBox is bound to an ObservableCollection, the ComboBox will be updated as soon as the collection is changed.
That's the advantage of using an ObservableCollection - you don't need to do any extra coding to update the UI.
If this is not the behavior you're seeing, perhaps you can post some code/xaml.
I'm still somewhat new to WPF (only done a few small projects with it). I'm trying to make a repeating group of controls (user can add/remove these groups), databound to a custom class. Example UI:
([UserButton1] [UserButton2]) <--each of these () is a separate group of buttons
([Cheese] [Wine] )
([Wallace] [Gromit] )
[Add] <--this button can add more groups
databound to a list of a class like this (pseudocode):
class UserButtons {
string UserButton1 = "UserButton1"
string UserButton2 = "UserButton2"
}
such as
List<UserButtons> = {
[0]: UserButton1, UserButton2
[1]: Cheese, Wine
[2]: Wallace, Gromit
}
I know this is the sort of thing WPF was created to do, but I can't quite figure out exactly how to go about it.
Should I use some sort of ListView? Would a DataTemplate help? A StackPanel sounds OK, but it doesn't have databinding for a list...or does it? And I'm not even sure how to make databinding work for the groups of buttons like indicated above (if that even made sense to you...sorry for the bad example). Does anyone have any insight on this problem?
I searched to try to find a question pertaining to this and didn't see one, perhaps because I wasn't sure what to search for. So, sorry if it's an unintended dupe.
I'm not entirely sure what you're looking for but I hope the example below helps. I used an ItemsControl whose ItemsSource is set to the collection of UserButtons. Its ItemTemplate property is set to a StackPanel that shows two buttons, the Content property of each is bound to properties in UserButtons.
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
</Window.Resources>
<StackPanel Orientation="Vertical">
<ItemsControl x:Name="itemsControl" Background="LightBlue">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="{Binding Button1}" Width="100"/>
<Button Content="{Binding Button2}" Width="100"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Width="50" Click="Button_Click">Add</Button>
</StackPanel>
</Window>
Code-Behind:
public partial class MainWindow : Window
{
ObservableCollection<UserButtons> oc;
public MainWindow()
{
InitializeComponent();
oc = new ObservableCollection<UserButtons>()
{
new UserButtons() { Button1="UserButton1", Button2 = "UserButton2"},
new UserButtons() { Button1="Cheese", Button2 = "Wine"},
new UserButtons() { Button1="Wallace", Button2 = "Gromit"},
};
this.itemsControl.ItemsSource = oc;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
oc.Add(new UserButtons() { Button1 = "NewButton1", Button2 = "NewButton2" });
}
}
public class UserButtons : INotifyPropertyChanged
{
private string button1;
public string Button1
{
get { return this.button1; }
set
{
this.button1 = value;
this.OnPropertyChanged("Button1");
}
}
private string button2;
public string Button2
{
get { return this.button2; }
set
{
this.button2 = value;
this.OnPropertyChanged("Button2");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}