C# WPF MVVM ComboBox Binding - c#

First of all, what I'm trying to do is a "simple" binding of a ComboBox to my source.
The structure is something like:
public class Data
{
public ObservableList<string> List {get;set;}
public string Selected {get;set;}
}
Also, it implements INotifyPropertyChanged interface.
My problem is, i found several solutions to do this via XAML, unfortunately i can't do it with XAML since my ComboBoxes have to be generated during runtime.
So my question is, how i can bind my ComboBox to Data.List, and also the selected item (value?) to Data.Selected, and this one should be TwoWay so my Data class knows that something was selected. Keep in mind this has to be through c# code (XAML is no option unfortunately).
Thanks in advance. :)

It's pretty easy. Assuming, that Data has properties instead of fields:
public class Data
{
public Data()
{
List = new ObservableCollection<string>
{
"Apple", "Orange", "Lime"
};
}
public ObservableCollection<string> List { get; private set; }
public string Selected { get; set; }
}
you can write this:
var comboBox = new ComboBox
{
DataContext = new Data()
};
comboBox.SetBinding(ComboBox.ItemsSourceProperty, new Binding("List"));
comboBox.SetBinding(ComboBox.SelectedItemProperty, new Binding("Selected")
{
Mode = BindingMode.TwoWay
});
To add ComboBox into visual tree, just call proper method for the container. E.g., this will work with any ContentControl (like Window):
AddChild(comboBox);

how i can bind my combobox to Data.List, and also the selected item (value?)
Create a custom composite user control which contains the combobox. Map the combobox's properties to two dependencies properties created on the custom control, one to load the data and the other to provide an on demand selected item's data. Any plumbing needs are done inside the codebehind which ultimately provides all the magic.
Then you can create/bind this control dynamically in codebehind as needed in the other page you are working on.

Sounds like a sort of "recursive binding". If your combos are in a container control, what you need is bound the container to a collection of your single combo model, so each view in the ItemsControl will be bound to a single combo model.

Related

Create a custom UserControl using c#

I Created a custom UserControl using Windows Form Control Library.And I want to create a property of UserControlwhich I can add item to it, then I can select item like comboBox.
WinForms allows you to create a rich design-time environment as well as providing for customised editors at runtime for certain properties that you define.
For example, if I plonk a MessageQueue component onto my WinForms form and view the Properties window, I can see a property named Formatter.
Clicking on the Formatter property however displays a drop-down box showing a preset list of values. This is an example of a UI Type Editor.
One way to do this is to define an enum for your supported values (it could be a dynamic list if you wish).
public enum Muppets
{
Kermit,
MissPiggy,
Fozzie
}
...then after defining your own editor derived from UITypeEditor (see MSDN link below)
class MyMuppetEditor : UITypeEditor { ... }
...you attach it to your control's property that you wish to have a drop-down as so:
[Category("Marquee")]
[Browsable(true)]
[EditorAttribute(typeof(MyMuppetEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public Muppets Muppet {get ; set; }
For more detailed information check out the link below.
More
Walkthrough: Implementing a UI Type Editor
Getting the Most Out of the .NET Framework PropertyGrid Control
EDIT: To allow for dynamic list, try making the property a string because that's what the selection will be bound to and during EditValue() when showing your SelectionControl just display a listbox of your dynamic items
You can do this by using the CategoryAttribute class.
Example:
[Description("Description of property here"), Category("Design")]
public bool my_property;
Check out the MSDN page for a more complete reference on how to use it.
EDIT: In the case of wanting to have a bool property, use this example.
private bool my_bool = true; // this is its default value
[PropertyTab("Property Tab Name")]
[Browsable(true)]
[Description("Description of Property"), Category("Data")]
public bool my_property
{
get { return my_bool; }
set { my_bool = value; }
}
I removed my last answer because I misunderstood your point.
An easy solution would require to make a Collection of enum as a property. The Designer property grid will automatically give you the choice among your initialized Collection with a ComboBox. The displayed names will also be the enum's name.
E.g. (something I made for a TextBox that only allows a certain type of value)
The enum :
enum EnumSupportedType
{
Integer = 1,
Double
}
The class where the property is located :
public class NumericTextBox : TextBoxBase, INumericControl
{
private EnumSupportedType _supportedType = EnumSupportedType.Integer;
public EnumSupportedType SupportedType {
get { return _supportedType; }
set { _supportedType = value; }
}
}
Then these items are suggested in a ComboBox (in the Designer property grid) :
Integer
Double
If you can't use enumerations, you can refer to Providing a Custom UI for Your Properties which seems to be a much harder solution to implement but will solve your problem.
I hope it will help you.

WPF Bind once and never update?

I have a ListView and a GridView that lists users in an application by names. Whenever the user selects an user to edit, I add a new tab to a TabControl, and bind all editable properties to the WPF controls.
However, when the user is editing in the Edit Tab, the information in the List (specifically, the name field) is also being updated.
Currently I'm making a copy of the object to be edited and leaving the original so it doesn't update the ListView, but isn't there a better/easier way to do this?
I've tried setting the Binding Mode=OneWay, didn't work, and also UpdateSourceTrigger=Explicit in the GridView but also didn't work.
Is there any easier way to do this?
Edit: The way I implemented my INotifyPropertyChanged class is part of the issue, since I have this:
public partial class MyTabControl : UserControl
{
public MyTabControl()
{
InitializeComponent();
//Here, DataContext is a List<Users>
//Users being my Model from the Database
//Some of it's properties are bound to a GridView
//User doesn't implement INPC
}
public void OpenTab(object sender, RoutedEventArgs e)
{
User original = (sender as Button).DataContext as User;
// - This will create a new ViewModel below with the User I'm sending
MyTabControl.AddTab(original);
}
}
And my ViewModel of Users is:
public class UserViewModel : INotifyPropertyChanged
{
public User Original { get; private set; }
public string Name { get { return Original.Name; } set { Original.Name = value; OnPropertyChanged("Name"); } }
public UserViewModel(User original)
{
Original = original ?? new User();
}
// - INPC implementation
}
Since my ViewModel is the one reporting the property changes, I didn't expect my original User to report it as well to the GridView.
The Mode=OneWay causes the information flow to go from the bound data entity to the target UI property only, any change to the UI property will not be bound back.
The reason why the UI content is changing is because the underlying property is read/write (i.e. has a getter and a setter) and is notifying any value change (due to the implementation of the INPC interface).
Presuming that it is a list of User objects you've bound to the GridView, you have two simple options to fix this. Which one is best depends on how much scope for change you have:
change the current Name property on the User object, remove the setter for it. Replace the setter with a method to set the property (i.e. SetUserName(string name)) which then sets the private member variable. Or pass the name as an argument to the constructor of the User entity.
create a new property with only a getter which returns the name and set your binding to that; i.e. public string UserName { get { return Name; }}. As there is only a getter there will be no notification of this property, so if the name does change it won't be propagated via this new property.

ListView with custom item layout in C#

I have a class called EventBox that extends TableLayoutPanel. It's a table with one single row and dynamically adjusting number of columns.
During its lifecycle, this EventBox adds/removes items from itself (buttons, combo boxes etc).
What I want is to create a ListView (or something similar) that would contain multiple EventBox objects and visually display them in a list.
I've created a class called TestEventList, but I do not know what to extend!
I've tried TableLayoutPanel (I believe it's overkill), ListBox (wrong!) and now ListView.
However, ListView's Items property has a method Add which only accepts ListViewItem objects as parameters.
How can I describe my EventBox as a ListViewItem?
Or better yet, what other choices do I have?
EDIT: I obviously want the list to be able to keep track of its items: add, remove at index etc.
Firstly, ListView will not do anything on its own. You need to set ListView.View to an instance of GridView.
I recently had to solve the dynamic column problem. The solution I chose is bindable and MVVM compatible, just in case you want to use that pattern (i was). I created a behavior (to avoid extending GridView) that will dynamically inject and remove columns as a source structure updates. This behavior needs dependency property that you bind to a instance of a class that defines the columns. The column class should allow you to define columns where a column is the property you are binding to on the source data, and a key (to represent the cell type).
public class ColumnDefinition
{
public string Key{ get; set}
public string ContentBindingPath { get; set;}
}
When the columns structure changes, the behavior builds and injects (or removes) columns into the attached GridView. The behavior builds each column based upon a series of key/value pairs defined on the behavior. This is to allow the XAML to specify the cell template to apply to the new columns, enforcing seperation of concerns.
public class CellTemplateDefinition
{
public string Key { get; set; }
public DataTemplate ColumnTemplate { get; set;}
}
public class DynamicColumnBehavior: Behavior<GridView>
{
public IEnumerable<ColumnDefinition> Columns
{
get { return (IEnumerable<ColumnDefinition>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
// Using a DependencyProperty as the backing store for Columns. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(IEnumerable<ColumnDefinition>), typeof(DynamicColumnBehavior), new UIPropertyMetadata(null));
public static void OnColumnsChanged(DependencyObject sender, DependencyPropertyChangedEventArgsargs)
{
DynamicColumnBehavior behavior = sender as DynamicColumnBehavior;
if(behavior != null) behavior.UpdateColumns();
}
public IEnumerable<CellTemplateDefinition> Cells { get; set; }
private void UpdateColumns(){ throw new NotImplementedException("I left this bit for you to do ;)");}
}

Changing display (UI) / order of items Observable Collection

EDIT #2:
public void ReverseOrderRegBtns()
{
ICollectionView view = CollectionViewSource.GetDefaultView(GlobalObservableCol.regBtns);
view.SortDescriptions.Add(new SortDescription("ReverseOrder", ListSortDirection.Ascending));
view.Refresh();
}
Not really sure how to fit the ICollectionView in, it also interrupts timers that are doing some logic. For some reason it interupts my timing that starts counting when a button gets created.
My application has several timers, so the buttons that get created get updates with possible new content every x amount of seconds.
EDIT:
The databinding looks as following:
<ListBox x:Name="lbRegistration" ItemsSource="{Binding regBtnsReverse, ElementName=Window}" Background="{x:Null}"
Old:
public ObservableCollection RegBtns
{
get
{
if (GlobalObservableCol.regBtns == null)
{
return new ObservableCollection();
}
return GlobalObservableCol.regBtns;
}
}
New (no longer works), the reverse is in the ObserableCol class
public IEnumerable<RegistrationButton> RegBtns
{
get
{
return GlobalObservableCol.regBtnsReverse;
}
}
I have created the IEnumerable in my a static class where the ObservableCollection list is available.
I cannot seem to bind when my databinding is a IENumerable. How to bind the NumbersReverse
method that is located in a other class to my MainWindow.xls?
I have a ObservableCollection that holds custom styled WPF buttons.
Those items get dynamically created and added on a custom slider control (listbox).
For example Button 1, Button 2, Button 3, ...
What i want is exactly the opposite.
For example Button 3, Button 2, Button 1.
This sounds like a linked list but my entire application is already using this Observable Collection throughout the entire logic.
When the buttons get displayed in the UI the last added button should be placed as the first item on my slider (that is a listbox control).
How exactly can i pull this off?
Thanks,
PeterP
<ListBox ItemsSource="{Binding Foo}"/>
private ObservableCollection<RegButtton> RegBtns = new...;
public ICollectionView Foo { get; set; }
Foo = CollectionViewSource.GetDefaultView(RegBtns);
Foo.SortDescriptions.Add(new SortDescription("SomePropertyName", ListSortDirection.Ascending));
Foo.Refresh();
Somewhere in your code there should be myCollection.Add( someButton ).
Replace it with myCollection.Insert( 0, myButton ) and you're finished.
I do not know what framework you use. I would do something like in my view model:
public ObservableCollection<int> Numbers { get; private set; }
public IEnumerable<int> NumbersReverse
{
get
{
return Numbers.Reverse();
}
}
and I would use NumbersReverse as a datasource:
<ListBox Name="listBox1" ItemsSource="{Binding NumbersReverse}"/>
Of course there are many other ways of doing that. You have to figure out what's best in your case...
Edit:
e.g. if you do not care about the order of the items for any other purpose, then the solution provided by stijn is much better.

How to Bind Listbox in WPF to a generic list?

i'm having trouble getting a clear answer for this.
I have a Static class (DataHolder) that holds a static list with a complex type (CustomerName and CustomerID properties).
I want to bind it to a ListBox in WPF but add another item that will have the word "All" for future drag and drop capablilities.
Anyone?
Create a ViewModel Class you can databind to! The ViewModel can reference the static class and copy the items to its own collection and add the all item to it.
Like this
public class YourViewModel
{
public virtual ObservableCollection<YourComplexType> YourCollection
{
get
{
var list = new ObservableCollection<YourComplexType>(YourStaticClass.YourList);
var allEntity = new YourComplexType();
allEntity.Name = "all";
allEntity.Id = 0;
list.Insert(0, allEntity);
return list;
}
}
}
Note, sometimes, you need empty Items. Since WPF can't databind to null values you need to use the same approach to handle it. The empty business entity has been a best practice for it. Just google it.
That "All" item has to be part of the list you bind your ListBox against. Natuarally you can not add that item to the DataHolder list because it holds items of type Customer (or similar). You could of course add a "magic" Customer that always acts as the "All" item but that is for obvious reasons a serious case of design smell (it is a list of Customers after all).
What you could do, is to not bind against the DataHolder list directly but introduce a wrapper. This wrapper would be your ViewModel. You would bind your ListBox agains a list of CustomerListItemViewModel that represents either a Customer or the "All" item.
CustomerViewModel
{
string Id { get; private set; }
string Name { get; set; }
public static readonly CustomerViewModel All { get; private set; }
static CustomerViewModel()
{
// set up the one and only "All" item
All = new CustomerViewModel();
All.Name = ResourceStrings.All;
}
private CustomerViewModel()
{
}
public CustomerViewModel(Customer actualCustomer)
{
this.Name = actualCustomer.Name;
this.Id = actualCustomer.Id;
}
}
someOtherViewModel.Customers = new ObservableCollection<CustomerViewModel>();
// add all the wrapping CustomerViewModel instances to the collection
someOtherViewModel.Customers.Add(CustomerViewModel.All);
And then in your Drag&Drop code somewhere in the ViewModel:
if(tragetCustomerViewModelItem = CustomerViewModel.All)
{
// something was dropped to the "All" item
}
I might have just introduced you to the benefits of MVVM in WPF. It saves you a lot of hassle in the long run.
If you use binding than the data provided as the source has to hold all of the items, ie. you can't databind and then add another item to the list.
You should add the "All" item to the DataHolder collection, and handle the 'All' item separately in your code.

Categories