Binding listbox from another project class - c#

How can i bind a property ObservableCollection, to xaml listbox.
Collection is in another project. I already have datacontext = this for second property.
I have some project Data, there is a class with property ObservableCollection Values. I need to bind it to mainwindow's xaml listbox Values.

If you have a view model that implements INotifyPropertyChanged
you can add an additional property that wraps the property in your other class, you would then need to bind to this property e.g.
const string MY_COLLECTION = "MyObservableCollection";
ObservableCollection<someType> _myObservableCollection;
public ObservableCollection<someType> MyObservableCollection
{
get
{
return _myObservableCollection;
}
set
{
if (value == _myObservableCollection) return;
_myObservableCollection = value;
RaisePropertyChanged(MY_COLLECTION);
}
}
In your xaml set your binding to yourDataContext.MyObservableCollection, in your (e.g) constructor for your view model set the property to your other observable collection i.e.
MyObservableCollection = OtherClass.OtherObservableCollection
Obviously their types will need to match

Related

Dotvvm run-time binding to 'object' type

i have this class
public class Property{
public string Name {get;set;}
public object Value {get;set;}
}
i want to create list of the above class List<Property> and dynamically add Mark Up Controls Code only
, so as their website they have an example HERE and what i did to that example is adding a public property of type Property to the TextBoxWithLabel class and changed the setter of the above example for binding as follows:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set {
SetValue(TextProperty, value);
Property.Value = value;
}
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
when i run the app and type something in the input field, the Value property of Type Property still null and here is where i'm stuck
i tried also to debug setter and it turns out it does not reach there so there is problem with run-time binding, which is 'as their example' this line of code
textBox.SetBinding(TextBox.TextProperty, GetValueBinding(TextProperty));
any help will appreciated :)
EDIT:
for more clarification,i have page called MainAppPage
and Markup Control with code behind called ContentControl
simply , MainAppPage passes List<Property> to ContentControl using this in MainAppPage
<controls:ContentControl Instance="{value: ClassObject}"/> then ContentControl start iterating through List<Property> and creating InputField's that derive from HtmlGenericControl
InputField's rendering like a charm in ContentControl
only thing is not working is binding , so again, how to bind Property.Value to InputField.Text so any changes happens in UI from user reflects on Property.Value after the InputField gets unfocused like any other MVVM pattern ?
DotVVM does not assign to the property usning the setter, is sets the underlying property store in DotvvmBindableObject instead. It's very simmilar what WPF does with their DependencyProperty, it's needed to represent the data bindings. You can actually completely omit the C# property declaration, declaring the field TextProperty and calling the DotvvmProperty.Register is enough to declare a property for dotvvm.
Other "problem" is that the controls do not store any data, everything has to be persisted in the view model. You can only use the control properties to data-bind a view model property. I think we are running here into a XY problem, I can only tell why your code does not work, but I have no idea what are actually trying to do...
Anyway, if you just want to "bind" your control to a view model property, have a look at https://www.dotvvm.com/docs/tutorials/control-development-markup-controls-with-code/2.0. You can declare the property like that:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
Use it in the markup of you control
#baseType FullName.Of.YourControl
{{value: _control.Text}}
And use the control in your page (or other control)
<cc:YourControl Text="{value: _this.Property.Value}" />

"Binding" wpf Combox selectedValue to an integer?

I just started a new wpf project in hopes that I could learn a new technique as opposed to using winForms all the time.
I seem to be having way too much difficulty binding the selected value of a comboBox to an integer variable in my "MainWindow" class.
I have been looking at a host of "simple" examples from sites like codeproject, but they all seem way too complicated to just return the selected value of a comboBox. I am used to setting the "SelectedValueChanged" property and just setting a variable, which takes just a few clicks, like so:
public int value;
public void comboBox_SelectedValueChanged()
{
value = comboBox.SelectedValue();
}
Is there a similarly sweet, simple, and short way to properly "bind" the selected comboBox item to an integer?
I am trying to understand how to use INotifyPropertyChanged but I keep getting errors when I try to use it. This is what I have so far, but to be honest, I'm not sure where I am going with it:
// Combo Box Value
public class ComboValue
{
#region Members
int valueToReturn;
#endregion
# region Properties
public int numWeeks
{
get { return valueToReturn; }
}
#endregion
}
// Veiw Model Class
public class ComboValueViewModel:INotifyPropertyChanged
{
#region Construction
public ComboValueViewModel()
{
}
#endregion
}
and I've never used "#region" before, I have no clue what that is.
Could someone fill me in if I'm headed down the right path here?
You don't mention how much you know of MVVM but here goes. Your view will have an associated ViewModel class. In here you'll expose a property containing the items to bind to the combobox, e.g.:
public List<ComboValue> ComboItems { get; set; }
If you populate this property in the VM's constructor, then a List<> is probably sufficient; however you'll often see an ObservableCollection<> used for this kind of thing - this comes into its own if you need to add or remove items within your VM code - your view will react to such changes and update the list accordingly. This won't happen with a List<>.
As for INotifyPropertyChanged, I haven't implemented this pattern in the above code snippet. Again, it's not strictly necessary if you populate the collection in the VM constructor and won't be re-assigning that property again. However it's good practice to use the INPC pattern on your VM properties. Without it, if you were to reassign that property elsewhere in your code, e.g.:-
ComboItems = aNewListOfItems;
then the view wouldn't be made aware of the property change, and the ComboBox wouldn't update. If you need this to happen then implement the INPC pattern on the property, e.g.:-
public List<ComboValue> ComboItems // or ObservableCollection<ComboValue>
{
get
{
return _comboItems;
}
set
{
if (_comboItems != value)
{
_comboItems = value;
OnPropertyChanged("ComboItems");
}
}
}
As you are working with a ComboBox, your VM should also expose a property that you bind to the control's SelectedItem property. This property should implement INPC, e.g.:-
public ComboValue SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
As you select items in the combo, the VM's SelectedItem property will change to reflect the current selection.
Finally, your XAML should end up looking something like this:-
<ComboBox ItemsSource="{Binding ComboItems}" SelectedItem="{Binding SelectedItem}" />
Hope this gives you a little "primer" into WPF binding! (Code snippets taken from memory so may not be 100% correct!).
Edit
Your ComboValue class exposes a numWeeks property. As it stands, you'll probably find that your ComboBox displays a list of ComboValue type names. To get the number to appear, the easiest thing is just to override .ToString() in your class and return the value of numWeeks. For more advanced formatting of items in controls such as this, you'll typically specify an ItemTemplate (again, plenty of examples can be found via Google!).

MVVM Properties in ViewModel?

I have a view which binds to a ViewModel
DataContext="{Binding MyViewModel, Source={StaticResource Locator}}">
the textboxes, ... are binding to the propertys IN the ViewModel:
<TextBox Text="{Binding MyValue, Mode=TwoWay}"/>
<TextBlock Text="{Binding DisplayValue}"/>
Property in ViewModel:
public MyViewModel()
{
DisplayValue = "0€";
MyValue = "0";
}
private string _myvalue;
public string MyValue
{
get
{
return _myvalue;
}
set
{
_myvalue = value;
ChangeValue();
RaisePropertyChanged(() => MyValue);
}
}
private string _displayvalue;
public string DisplayValue
{
get
{
return _displayvalue;
}
set
{
_displayvalue = value;
RaisePropertyChanged(() => DisplayValue);
}
}
private void ChangeValue()
{
//do something here and change the values of the property, e.g.:
DisplayValue = MyValue + "€";
}
This is just a snipped. I normally have ~50 properties IN THE VIEWMODEL and all the methods are also in the ViewModel (means RelayCommands AND methods, which will be called in the setter of ~50% of the properties).
As you can see, I'm not using any Model(s). Is this a normal way of using MVVM or should I create a new class and put all the properties/methods in the new class (Model)?... But how am I supposed to bind the elements in the view with the Properties in the Model, when the views DataContext is binded to the ViewModel?
Edit: To make it clear.
I have a TextBox and the TextBox is binded to a property in the ViewModel. Is this the correct way of using MVVM? Should I use a Model-Class only when I have a List (e.g. ComboBox) or also when I have several TextBox (which would be in my eyes kinda stupid and unnecessary)?
I hope I understand what you are trying to do. My solution comprises of the DependencyProperty that I use in the MVVM pattern, not the INotifyPropertyChanged.
Lets say, you have a model, that contains a property:
public class SymbolStatsModel
{
public string Symbol
{
get
{
return this._symbol;
}
set
{
this._symbol = value;
}
}
}
Then the corresponding ViewModel is going to be like this. Declare a property and a dependency property:
public string Symbol
{
get
{
return (string)GetValue(SymbolProperty);
}
set
{
SetValue(SymbolProperty, value);
}
}
public static readonly DependencyProperty SymbolProperty =
DependencyProperty.Register
(
"Symbol",
typeof(string),
typeof(SymbolStatsViewModel),
new FrameworkPropertyMetadata
(
string.Empty
)
);
And also create a property of the Model class(SymStatsModel) in the ViewModel:
public SymbolStatsModel SymbolStatsModel
{
get
{
return new SymbolStatsModel(Symbol);
}
set
{
this.Symbol = value.Symbol;
}
}
In that way, the values that you assign to the ViewModel Symbol Property are going to be assigned to the Model Property. Also, you can directly access the Model's properties from the View by accessing the property of the Model present in the ViewModel.
This may be a little hard to grasp, but this sure is the way to make the view communicate with the Model. On another thought, you can specify the property of the Model just like I have mentioned in my solution, while using the INotifyPropertyChanged. A little immature I guess, but you gave give it a thought.
"Divide and Conquer" should help you in maintainability
Try and find boundaries in your UI, logical groups, repeating parts.
Factor them out
You can always reset the DataContext, or use a more complex Binding Path like MyVm.SubVm.Property

Applying where on ObservableCollection doesn't reflect on datagrid

I try to "filter" an ObservableCollection and update the bound DataGrid.
ObservableCollection<Record> recordObservableCollection;
recordObservableCollection = new ObservableCollection<Record>(GetData()); //GetData() returns IEnumerable<Record>
dataGrid1.ItemsSource = recordObservableCollection;
Then I try to filter this collection:
recordObservableCollection = new ObservableCollection<Record>(recordObservableCollection.Where(filter));//filter is Func<Data.Record, bool>
recordObservableCollection is updated fine.
But the DataGrid is not updated.
Your field or variable called recordObservableCollection has one value initially and a different value after filtering. Because you used new ObservableCollection<Record>(...) twice you created two separate observable collection instances.
The problem is that the DataGrid is still referring to the first instance. Even though you have changed recordObservableCollection, that only affects its value. The value of DataGrid.ItemsSource is still what it was before the filtering.
To fix this problem, you need to re-assign the new collection's value to the ItemSource property. Simply repeat what you did the first time, but after the filtering:
dataGrid1.ItemsSource = recordObservableCollection;
and now ItemSource will be set to the new value of recordObservableCollection.
ObservableCollection will get update because ObservableCollection (System.Collections.ObjectModel) throws an event every time the collection get changed but you have to set the filter collection as itemsource again other wise it wont update the UI...
The best way to do this use a public property that you'll bind in control as item source and in that property you will define NotifyPropertyChanged in setter . Every time you'll change the collection using this property the control will also be updated ...
Let Suppose you have your data grid in test.xaml
--> First fo all work for INotifyPropertyChanged add an abstract class in your project inherit it from INotifyPropertyChanged interface and define OnPropertyChanged method
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
--> After add a class in your project named it testViewModel which will inherit your ViewModelBase Class..
--> Now in testViewModel you'll make a property for your grid binding like this
Private ObservableCollection<Record> _recordObservableCollection
public ObservableCollection<Record> recordObservableCollection
{
get
{
if(_recordObservableCollection == null)
{
_recordObservableCollection = new ObservableCollection<Record>(GetData());
recordObservableCollection = new ObservableCollection<Record>(recordObservableCollection.Where(filter));
}
return _recordObservableCollection;
}
Set
{
_recordObservableCollection = Value;
OnPropertyChanged("recordObservableCollection"); //Your property name
}
}
here now if u update your collection using property on any other property, method or command UI will be updated beacsue in setter you have defined OnPropertyChanged...
Now comes back to test.xaml here you have to do two things
Set dataContext of test.xaml either in code behing or in in xaml
(In Code behind just after InitializeComponent() make an intance of viewmodel class and assign it as DataContext like this
public test()
{
InitializeComponent();
testViewModel vm = new testViewModel();
this.DataContext = vm;
}
Bind property you defined in testViewModel to grid
<Grid Name="MyGrid" DataContext="{Binding recordObservableCollection}">
</Grid>
Expose the ObservableCollection<Record> as a public property
also
Using ObservableCollection only affect binding when you add/remove items from your list. By using ObservableCollection you do not need to reset binding to the list or DataGrid when your collection changed (not the item inside collection changed). But they do not have any effect when your data object properties changed. For that you need to implement INotifyPropertyChanged interface for your DataObject.

WPF - Binding in XAML to an object created in the code behind

Can an object created in the code (ie C#) be used for binding in the XAML?
For example:
public class MyForm
{
private MyComplexObject complexObject;
public MyForm()
{
InitializeComponent();
}
public OnButtonClick(object sender, RoutedEventArgs e)
{
complexObject = new MyComplexObject();
}
}
complexObject is not created till a button is clicked. But once that button is clicked I would like to have a text box that is bound to complexObject.ID start showing the Id.
I would like to do that in the XAML if that is possible.
Can this be done? If so, how?
Yes, this can be done, binding to a property that you update with the desired value. I'd suggest you look into the MVVM pattern (Model-View-ViewModel), which is really useful for structuring this nicely working with WPF. Check out this video for a nice overview:
MVVM video
Using MMVM you would create a class which would be the ViewModel class. This one would typically be set to the DataContext of the View. Having done so you could add dynamic references to the other properties of the class, e.g. binding your text field to some property holding the Id og the ComplexObject. If your ViewModel class had a property ComplexObject, which again had a property ID you'd simply bind to the object like this:
<TextBlock Text="{Binding ComplexObject.ID}" />
Having this you could trigger creation of your ComplexObject from mouse click, which you should ideally set up as a command binding. Also note that the ViewModel class (or whoever is holding the ComplexObject needs to notify the View when the object has been set. This can either be done by making the ComplexObject a DependencyProperty or by making the class holding the property implement the INotifyPropertyChanged interface - giving it the PropertyChanged function to trigger the changed event. I prefer the latter.
One possibility would be to have your XAML bind to a property on your code behind. The getter for that property would return complexObject.ID, if complexObject != null. Otherwise, it returns something "default", whether that's null or 0 or default(ID's type). Similarly, the setter for that property would assign value to complexObject.ID if complexObject is, again, not null.
public int ID
{
get
{
if (complexObject != null)
return complexObject.ID;
return 0; // or null or some appropriate default
}
set
{
if (complexObject != null)
complexObject.ID = value;
}
}

Categories

Resources