Binding WPF combo box to specific dictionary instance - c#

I am new to WPF and I am trying to get databinding to work with a combo box. I have a class I created called FolderList that basically wraps around a FileSystemWatcher instance. It has a property called Folders that returns a dictionary of the folder names and their full paths.
Then for the class for my WPF window it has contains an instance of FolderList called FolderWatcher that is configured in the constructor. I would like to databind a combobox to the dictionary in that specific instance of FolderList.
I've found examples where there are static instances of data objects or where they are created through XAML but I can't figure out how to bind to a specific instance.
I am not picking if this is done in XAML or C#. I've basically gotten this far with the ObjectDataProvider.
<Window.Resources>
<ObjectDataProvider x:Key="ProjectNames"
ObjectType="{x:Type local:FolderList}"
/>
</Window.Resources>
And here is the combo box I want to data bind. This doesn't produce any errors but it also isn't populated. I know enough to know I am missing something major in the ObjectDataProvider but I just don't know what it is.
<ComboBox Name="ProjectCombo" MinWidth="100" ItemsSource="{Binding Source={StaticResource ProjectNames}, Path=Folders}" />

You don't need a the ObjectDataProvider here. Just set ItemsSource of your ComboBox directly in the Window's contructor, where you initialize your instance of FolderList:
public MyWindow()
{
InitializeComponent();
FolderWatcher = new FolderList(...);
ProjectCombo.ItemsSource = FolderWatcher.Folders;
}
Another option would be to set the instance of FolderList as DataContext of your window and then use binding to set ItemsSource of theComboBox`:
public MyWindow()
{
InitializeComponent();
FolderWatcher = new FolderList(...);
DataContext = FolderWatcher;
}
<ComboBox Name="ProjectCombo" MinWidth="100" ItemsSource="{Binding Folders}" />
I suggest you look into MVVM pattern. If you designed your application according to that pattern, you would have a View Model instance as DataContext of your view and that View Model would expose a property you could bind to.

Related

Display a List<T> by using x:Bind?

I'm currently trying to figure out how to use the x:Bind stuff in UWP/xaml.
I'm learning more everyday and the app I'm curently writing is getting way easier to manage since I can implement the MVVM patterns now.
But there is one thing I encountered now...
I have a List<T> which I want to display in my UI. How can I bin this list while using x:Bind?
Or do I have to convert it into something else first?
Best Regards,
Daniel
You can certainly use a List<T> for binding, but usually ObservableCollection<T> is preferable, because it also allows the UI to observe list changes, as opposed to List<T> that will not update after bound first. You can create an ObservableCollection from List using the constructor:
ObservableCollection<T> data = new ObservableCollection<T>( list );
In any case, you first have to create a property (but fields are also supported with x:Bind) in your view model:
public ObservableCollection<T> Data { get; } = new ObservableCollection<T>();
Remember that binding connects to the instance, so if you would set a new instance to Data property, it the binding would not update. For that to work you need to implement INotifyPropertyChanged interface on your view mdoel and raise PropertyChanged event in the setter.
Now, to display the items in your UI you need a list control like ListView or GridView and bind it to your collection:
<ListView ItemsSource="{x:Bind Data, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- your template -->
<TextBlock Text="{Binding SomePropertyOfT}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You can find a thorough walk through data binding in documentation as well.
If your desire is to have a collection which might change over time, and you want the UI to be notified of such modifications, you should not utilize List<T>!
Use instead ObservableCollection<T>, which implements the INotifyPropertyChanged and INotifyCollectionChanged which is the heart of the MVVM, allowing the ViewModel/View to communicate between each other.
Such collection automatically handles the addiction/removal of elements automatically for you!
Anyway, here is a simple example, showing how you can use a List to communicate with your View.
MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
RandomList = new List<string>() {
"random 1", "random 2", "random 3"
};
}
List<string> RandomList { get; set; }
MainPage.xaml:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ComboBox ItemsSource="{x:Bind RandomList}"
PlaceholderText="List of Random things"/>
</Grid>
Here we defined a {x:Bind } to populate the ItemsSource's dependency property, which accepts a Collection.
This Binding is defined with the default binding Mode, which is OneTime for compiled bindings. For instance, if you were to perform Bindings with {Binding } markup, the default mode is OneWay;
Since you created a Collection with List rather than ObservableCollection, there would be no reason to specify any other mode than the default, since you haven't implement a mechanism for your CLR collection to actually notify the View of an update.

WPF: Cannot get View to change dynamically using DataTemplate

I have a problem designing my WPF application. I cannot get the Views to change dynamically. The code to call another View is contained in the Views themselves. (I am trying to implement the MVVM pattern. I do not want any code behind in the View xaml files other than assigning the DataContext. An exception is made in the xaml file of the MainWindow).
Basically, I have a Window that contains a UserControl. The UserControl is my View and it is connected to another class serving as ViewModel through Datacontext.
What I want to do is to dynamically change this View/ViewModel pairs contained in the Window.
My idea was define a static property in the ViewModel of the MainWindow and store the ViewModel of the current View in it. Then I planned to use DataTemplates to automatically load a new View whenever a new ViewModel is stored in the static property.
I decided to use a static property because the code to load another ViewModel is contained in the ViewModels itself and I needed a central point with access from everywhere.
So far so good. My initial View loads and displays correctly.
However, pressing a button in that View to load the next View fails although the new ViewModel is correctly assigned to the static property.
I tried several things.
I defined DataTriggers within the ContentControl to react to changes in the static property. No help.
Implementing INotifyProperty and DependencyProperty failed in the end because of the static nature of the property (or I did something wrong).
I just can’t get it to work.
Do you have any ideas why this would be?
Do you have an idea how I could solve my general problem of dynamically displaying Views without using a static property in my MainWindow. I believe this is causing problems and I have a notion that I am not using the most elegant method. (I do want to maintain the concept of each View holding the code to load any other View)
This is a code fragment from the MainWindow:
<UserControl>
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:StartViewModel}">
<v:StartView></v:StartView>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:OverviewViewModel}">
<v:OverviewView></v:OverviewView>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentControl Content="{Binding ActiveViewModel}">/ContentControl>
</Grid>
</UserControl>
Code behind:
DataContext = new MainViewModel();
MainViewModel contains the definition for the property ActiveViewModel. The constructor for the class is static. All ViewModels inherit from BaseViewModel class:
private static BaseViewModel activeViewModel;
static public BaseViewModel ActiveViewModel
{
get { return activeViewModel; }
set { activeViewModel = value; }
}
Thanks a lot for your help.
Bye,
Eskender

Failing to successfully expose my ViewModel data to my View for XAML binding

I'm building a Windows Universal app and trying to expose data from my ViewModel to my View so that I can bind it to XAML elements. I have completely commented out all of my code at this point and am just writing lines of test code to try and get it to work, that is what is in the examples below. Binding directly from the View (if I create an object there as a test) does work.
Please help me to understand where I am going wrong, I think I've read every binding tutorial on the internet and still just don't get it.
View (MainPage.xaml.cs):
public MainPage()
{
InitializeComponent();
DataContext = new MainViewModel();
}
ViewModel (MainViewModel.cs):
public class MainViewModel
{
public Term newTerm = new Term
{
TermName = "Table",
TermDescription = "You eat dinner on it"
};
}
XAML (MainPage.xaml):
<StackPanel DataContext="{Binding newTerm}" x:Name="mvvmStack" Orientation="Vertical">
<TextBlock x:Name="mvvmTermName" Text="{Binding TermName, FallbackValue='Fallingback'}" />
<TextBlock x:Name="mvvmDescription" Text="{Binding TermDescription, FallbackValue='Fallingback', TargetNullValue='Unknown'}" />
</StackPanel>
The error I get is:
Error: BindingExpression path error: 'newTerm' property not found on ''. BindingExpression: Path='newTerm' DataItem=''; target element is 'Windows.UI.Xaml.Controls.StackPanel' (Name='mvvmStack'); target property is 'DataContext' (type 'Object')
I have read about this type of error and although I have some idea of what it is trying to say I cannot work out how to fix it. I'm very much a complete beginner with coding, especially C# so please take that into account when answering :-)
Just try to change it from field to a property and it will be working correctly. You can't bind to fields.
EDIT:
private Term _term;
public Term NewTerm{
get{return _term;}
set
{
_term= value;
OnPropertyChanged("Term");
}
}
if you need to add notify the view of changes in the viewmodel you need to implement INotifyPropertyChanged.
check this answer it will provide an example for property changed. https://stackoverflow.com/a/27685925/1448382
If you want to bind the view to sub properties, you have two options depending on the situation:
1- Relative Binding: this scenario is used when you will not modify the properties inside the Term object from the ViewModel i.e. they will be just initialized in the viewmodel and can be modified in the view, just like the way you are doing it. Plesae note, that anything you need to bind to should be a property and not a field.
2- Binding to Viewmodel directly: this scenario is used when you will modify the properties inside the Term object from the Viewmodel after the view load. This way you will need to add properties to the viewmodel for the properties TermName and TermDescription.
public string TermName{
get{return NewTerm.Name;}
set{NewTerm.Name = value;
OnPropertyChanged("TermName");
}//The same is applied for TermDescription
But be aware that you will need to remove the binding on the Stackpanel object since you have defined the properties directly in the Viewmodel.
Try something like that:
<Page.Resources>
<viewModels:MainViewModel x:Key="MainViewModel" />
</Page.Resources>
And then:
<StackPanel x:Name="mvvmStack" Orientation="Vertical">
<TextBlock x:Name="mvvmTermName" Text="{Binding newTerm.TermName, Source={StaticResource MainViewModel} FallbackValue='Fallingback'}" />
<TextBlock x:Name="mvvmDescription" Text="{Binding newTerm.TermDescription, Source={StaticResource MainViewModel} FallbackValue='Fallingback', TargetNullValue='Unknown'}" /></StackPanel>
Of cource newTerm should be an property with INotifyChanged

Getting parent of new tab after adding to bound TabControl (mvvm)

I'm adding a close button to my tabs using the following guide:
http://www.codeproject.com/Articles/84213/How-to-add-a-Close-button-to-a-WPF-TabItem
This has become a problem because the event uses the 'parent' of the added tab to remove that tab from the tabcontrol. I'm binding the tab control using mvvm, so the parent property is apparently not being set and giving me a null reference exception for the parent when the event tries to remove from it.
Here's the binding so you get the idea:
<TabControl Name="tabControl" Margin="0,22,0.2,-5.2" ItemsSource="{Binding Tabs}" Background="#FF4C76B2"/>
Heres where the tabs are being added.
private void AddTab(object tabName)
{
ClosableTab newTab = new ClosableTab();
newTab.Title = "title?";
//newTab.Header = tabName;
TextBox test = new TextBox();
test.Text = "CONTENT (" + tabName + ") GOES HERE";
newTab.Content = test;
Tabs.Add(newTab);
OnPropertyChanged("Tabs");
}
Here is the event where the null reference is taking place:
void button_close_Click(object sender, RoutedEventArgs e)
{
((TabControl)this.Parent).Items.Remove(this);
}
As I see it there are two options:
try to find another way to remove the tab (without the parent
property)
try to find a way to somehow set the parent property (which cant be
done directly, it throws a compiler error)
That doesn't sound like MVVM to me. We work with data, not UI elements. We work with collections of classes that contain all of the properties required to fulfil some requirement and data bind those properties to the UI controls in DataTemplates. In this way, we add UI controls by adding data items into these collections and let the wonderful WPF templating system take care of the UI.
For example, you have a TabControl that we want to add or remove TabItems from... in a proper MVVM way. First, we need a collection of items that can represent each TabItem:
public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(TestView));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
I'm just using a DependencyProperty because I knocked this up in a UserControl and I'm just using a collection of strings for simplicity. You'll need to create a class that contains all of the data required for the whole TabItem content. Next, let's see the TabControl:
<TabControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemTemplate}" />
We data bind the collection to the TabControl.ItemsSource property and we set the TabControl.ItemTemplate to a Resource named ItemTemplate. Let's see that now:
xmlns:System="clr-namespace:System;assembly=mscorlib"
...
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type System:String}">
<TabItem Header="{Binding}" />
</DataTemplate>
This DataTemplate defines what each item in our collection will look like. For simplicity's sake, our strings are just data bound to the TabItem.Header property. This means that for each item we add into the collection, we'll now get a new TabItem with its Header property set to the value of the string:
Items.Add("Tab 1");
Items.Add("Tab 2");
Items.Add("Tab 3");
Note that I included the System XML Namespace Prefix for completeness, but you won't need that because your DataType will be your own custom class. You'll need more DataTemplates too. For example, if your custom class had a Header property and a Content property, which was another custom class, let's say called Content, that contained all of the properties for the TabItem.Content property, you could do this:
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type YourPrefix:YourClass}">
<TabItem Header="{Binding Header}" Content="{Binding Content}" />
</DataTemplate>
<DataTemplate DataType="{x:Type YourPrefix:Content}">
<YourPrefix:SomeUserControl DataContext="{Binding}" />
</DataTemplate>
So this would give you TabItems with Headers set and Content that comes from SomeUserControl which you could design. You don't need to use UserControls, you could just add more UI controls to either DataTemplate. But you will need to add more controls somewhere... and more classes and properties, always remembering to correctly implement the essential INotifyPropertyChanged interface.
And finally, to answer your question in the proper MVVM way... to remove a TabItem, you simply remove the item that relates to that TabItem from the collection. Simple... or it would have been if you really had been using MVVM like you claim. It's really worth learning MVVM properly as you'll soon see the benefits. I'll leave you to find your own tutorials as there are many to chose from.
UPDATE >>>
Your event handling is still not so MVVM... you don't need to pass a reference of any view model anywhere. The MVVM way is to use commands in the view model. In particular, you should investigate the RelayCommand. I have my own version, but these commands enable us to perform actions from data bound Buttons and other UI controls using methods or inline delegates in the view model (where action and canExecute in this example are the CommandParameter values):
<Button Content="Close Tab" Command="{Binding CloseTabCommand}"
CommandParameter="{Binding}" />
...
public ICommand CloseTabCommand
{
get { return new ActionCommand(action => Items.Remove(action),
canExecute => canExecute != null && Items.Contains(canExecute)); }
}
So whatever view model has your Tabs collection should have an AddTabCommand and a CloseTabCommand that add and remove items from the Tabs collection. But just to be clear, for this to work properly, your ClosableTab class should be a data class and not a UI control class. Use a DataTemplate to specify it if it is a UI control.
You can find out about the RelayCommand from this article on MSDN.

Why does binding to a DepenencyProperty on a user control use the user control's Data Context?

Say I have a WPF application (exe) that has this in the MainWindow.xaml:
<Grid>
<extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}"/>
<extraControls:MyUserControl MyDependencyProperty="{Binding Something}" />
</Grid>
and my MainWindow.xaml.cs looks like this:
public MainWindow()
{
DataContext = new MainWindowVM();
InitializeComponent();
}
And my MainWindowVM.cs has a property setup for Something that notifies on property changed.
The user controls are made in a separate dll. As you may guess, MyMVVMUserControl has the DataContext set to a view model.
public MyMVVMUserControl()
{
DataContext = new MyMVVMUserControlVM();
InitializeComponent();
}
MyUserControl does not have a DataContext set in the code behind.
So the interesting thing is that they both have MyDependencyProperty setup exactly the same.
But the MVVM version does not work.
After digging in a bit, I found that the {Binding Something} in MainWindow.xaml is using the View Model setup for the MyMVVMUserControl as the DataContext (rather than the DataContext set in MainWindow.cs (set to MainWindowVM)).
And my question is why?
Why would WPF look inside the user control and use it's DataContext for a binding that is in the actual application?
(NOTE: I know I can get around this by setting the source in the binding, but I want others to be able to use my user controls. But with this issue, I now have a built-in "gotcha" for anyone I want to use my user controls.)
I think I understand you problem, and I'm gonna to give a solution that works for me (I had this problem before). The think is that seams that you are setting the DataContext for you MyMVVMUserControl in code behind, and then it take the bindings from that.
The solution I found for this, is to set the datacontext in code behind, but not at the user control. Set the datacontext for the UserControl's child item. For instance, supose this is the Xaml of your UserControl:
<UserControl ... x:Name="userControl">
<Grid x:Name="rootContainer">
...
</Grid>
</UserControl>
Then in the code behind set the rootContainer's data context, in this way all visual children can access to the control data context, and also the user control datacontext is empty.
...
rootContainer.DataContext = new UserControlViewModel();
...
Hope this may helps you to solve your issues...
You really shouldn't ever set the DataContext of a UserControl from inside the UserControl. By doing so, you are preventing any other DataContext from getting passed to the UserControl, which defeats one of WPF's biggest advantages of having separate UI and data layers.
WPF objects only inherit their DataContext from the parent object if the DataContext is not set to anything else. When your MyMVVMUserControl is being created, you are setting the DataContext to a new MyMVVMUserControlVM, which prevents the DataContext from being inherited from MainWindow.
So its normal that your MVVMUserControl would have it's DataContext set to your MyMVVMUserControlVM, because you set it explicitly in the UserControl's constructor.
This is by-design. UI objects in WPF/MVVM are only meant to be visual representations of the data layer, so it wouldn't make much sense to set the data layer and then try to bind your properties to something that is not on the data layer.
For example, take this line of code:
<UserControl DataContext="{Binding ClassA}" Content="{Binding Name}" />
This would bind the Content property to UserControl.DataContext.Name, which is ClassA.Name. It wouldn't make much sense if this would result in binding to UserControl.Parent.DataContext.Name, as the binding should refer to to the current object's DataContext, and not the Parent's DataContext.
So the only time I ever set the DataContext of a UserControl from inside the UserControl itself is if the UserControl is its own separate object that is never meant to interact with data from the rest of the application. Which so far has been never :)
Typically my UserControls are almost always one of two things:
Either a visual representation of a ViewModel (or Model), such as a CustomerUserControl for a CustomerViewModel, in which case I pass them the DataContext they need when they get used
For example,
<local:CustomerUserControl DataContext="{Binding SelectedCustomer}" />
or
<DataTemplate DataType="{x:Type local:CustomerModel}">
<local:CustomerUserControl />
</DataTemplate>
Or a self-sustained UI object that receives any external data it needs via custom DependencyProperties, and does any additional logic in the code-behind the control, such as a DatePicker control that has a SelectedDate dependency property, or a CalculatorUserControl with dependency properties for Equation and Value
<local:DatePickerUserControl SelectedDate="{Binding SomeDate}" />
<local:CalculatorUserControl Equation="{Binding SomeString}"
Value="{Binding SomeDouble}" />
In your case, it sounds like you should be using the first case, and should be passing a ViewModel into your UserControl containing the data it needs.
<extraControls:MyMVVMUserControl DataContext="{Binding MyMVVMUserControlVM}"
MyDependencyProperty="{Binding Something}">
or
<extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}">
<extraControls:MyMVVMUserControl.DataContext>
<viewModels:MyMVVMUserControlVM />
</extraControls:MyMVVMUserControl.DataContext>
<extraControls:MyMVVMUserControl />

Categories

Resources