How to bind nested components together Treeview,textbox & treeview? - wpf

I come from How to use custom styles to bind with custom command?
Here's my situation. I have a custom TabControl. And the ItemsSource is in a ViewModel.[Header and Content]. Now the Content is just simple strings.
public ViewModel()
{
TabItems = new ObservableCollection<TabData>();
//dummy data
TabItems.Add(new TabData() { Header = "Tab 1", Content = "Tab content 1" });
But I'd like to make the Content to be some other controls. To say a Grid with Textbox and Treeview inside.
What's the ideal way to do this these kind of bindings?
I am thinking about create a userControl to hold the grid with textbox and treeview inside, and in the original ViewModel, I can write Header = "Tab 1", Content = new UserControl()

Here is how you can implement view model for nested components
I'll demo the same by displaying a user control in Tab 2 via MVVM
so let's start by defining a view model class
public class Tab2ViewModel : ViewModelBase
{
public Tab2ViewModel()
{
TabCommand = new SimpleCommand(OnTabCommand);
}
public ICommand TabCommand { get; private set; }
private void OnTabCommand(object obj)
{
TabText = "Button was clicked";
}
private string _tabText;
public string TabText
{
get { return _tabText; }
set
{
_tabText = value;
RaisePropertyChanged();
}
}
}
ViewModelBase class in the code above is an implementation of INotifyPropertyChanged providing a method RaisePropertyChanged to notify the property changes
lets define a view for this view model
<UserControl x:Class="test_tab_control5.Views.Tab2View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<TextBlock Text="Tab 1"
FontWeight="Bold" Margin="10"/>
<TextBlock Text="{Binding TabText,FallbackValue=tab text}" Margin="10"/>
<Button Command="{Binding TabCommand}"
Content="Click me" HorizontalAlignment="Center"/>
</StackPanel>
</UserControl>
now lets define a data template for the same
<DataTemplate DataType="{x:Type vm:Tab2ViewModel}">
<vw:Tab2View />
</DataTemplate>
this is how you can define a template for the view models, in this case I a defining for Tab2ViewModel. in code above the namespaces defined as xmlns:vm="clr-namespace:test_tab_control5.ViewModels" & xmlns:vw="clr-namespace:test_tab_control5.Views"
finally the usage
TabItems.Add(new TabData() { Header = "Tab 2", Content = new Tab2ViewModel() });
note that I am setting the content as Tab2ViewModel instead of any user control, this allows me implement a loose coupling between the view model and the view
and as a result WPF will resolve the user control as the template for the Tab2ViewModel
result
here is a full working sample for the above code: MVVM TabControl.zip
MD5 Checksum: 4BA61028B5179AA884ECAC21D69A816A
this sample is based on the sample shared in your previous question How to use custom styles to bind with custom command?
With the working sample I have also attempted to show couple of ways to define a template and usage of resource dictionaries

Related

How to show floating virtual keyboard (user control) in MainWindow when an input control (from another user control) has been set to focus in WPF?

I have been doing development work in WPF application which uses an MVVM pattern for a couple of days now. I'm very new to WPF and MVVM pattern as well.
In my scenario, I have a user control view (named EPayView.xaml) which has a textbox that will accept a phone number. The view has a corresponding viewmodel (named EPayViewModel.cs). In the MainWindow.xaml, I have a user control (floating virtual keyboard) which is derived from namespace controls WpfKb.Controls. The MainWindow.xaml also has a corresponding viewmodel (named MainViewModel.cs)
Having said that, I have done research on how to use attached dependency properties which lead me to this solution. Set focus on textbox in WPF from view model (C#) which I believe this is where I could bind the property IsFocused in the textbox of EPayView.xaml.
Below are the codes that I have already incorporated in my solution.
EpayView.xaml (textbox xaml markup)
<TextBox Text="{Binding PhoneNo}" Grid.Row="5" Margin="10,0,10,0" VerticalContentAlignment="Center" FontSize="12" x:Name="Email" behaviors:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>
MainWindow.xaml (xaml markup)
<Window x:Class="SmartPole540.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
xmlns:square="clr-namespace:SmartPole.View.Square;assembly=SmartPole.View"
xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
Title="CitiPulse"
WindowStartupLocation="Manual"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
Name="mainWindow">
<userControls:RollPanel.BottomContent>
<square:SquareView Canvas.Top="1010" DataContext="{Binding DataContext.SquareViewModel,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type userControls:RollPanel}}}"/>
</userControls:RollPanel.BottomContent>
<controls:FloatingTouchScreenKeyboard
x:Name="floatKb" Width="500" Height="250" PlacementTarget="{Binding ElementName=MainGrid}"
Placement="Center" AreAnimationsEnabled="False" Visibility="Visible"
IsOpen="{Binding IsChecked, ElementName=kbButton}"/>
</Window>
In the above code, the user control RollPanel.BottomContent host the EPayView.xaml view inside another view which is RollPanel.xaml
EpayViewModel.cs contains the static class FocusExtension for the IsFocused attached property (refer to this solution - Set focus on textbox in WPF from view model (C#)). And, EPayViewModel.cs already implemented INotifyPropertyChanged which is wrapped inside a concrete class ObservableObject that accepts type of T. This is also same with MainViewModel.cs
public class EPayViewModel : ObservableObject<EPayViewModel>, IPaymentViewModel, IActiveViewModel
{ ... }
public class MainViewModel : ObservableObject<MainViewModel>
{ ... }
As such, my goal is that when the textbox in EPayView.xaml has the focus, the floating virtual keyboard (floatKb) in the MainWindow.xaml will be shown.
I'm stuck on how to proceed (I was thinking if a call to FocusExtension static class in EPayViewModel inside my MainViewModel.cs will suffice?), any help is greatly appreciated.
Cheers,
As AnjumSKhan already said, to react to some event in a MVVM way, you'll have to use Command. Command can be called within an EventTrigger, you will need to add a Reference to System.Windows.Interactvity component.
Let's assume you have a simple View and View Model and you need to show this View when the TextBox in a MainWindow got focus.
View (NewWindow.xaml)
<Window x:Class="My.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<TextBlock Text="{Binding Message}"/>
View Model
public class NewWindowViewModel
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; }
}
}
You also have a MainWindow, it is a main view for an app and it contains the target TextBox. You may see that there is an EventTrigger added to the TextBox and it has a property InvokeCommandAction which is binded to the MainWindowViewModel's command called ShowCommand.
Main Window
<Window x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="350" Width="525">
<TextBox Height="40" Text="{Binding Text}">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="GotFocus">
<Interactivity:InvokeCommandAction Command="{Binding ShowCommand}"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</TextBox>
In the Show method of MainWindowViewModel NewWindow view is created and got new NewWindowViewModel instance as a DataContext. RelayCommand class is presented in my answer to this question
MainWindowViewModel
public class MainWindowViewModel
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
private ICommand _increaseCommand;
public ICommand ShowCommand
{
get
{
if (_increaseCommand == null)
{
_increaseCommand = new RelayCommand(
p => true,
Show);
}
return _increaseCommand;
}
}
private void Show(object obj)
{
var w = new NewWindow();
var nvm = new NewWindowViewModel();
nvm.Message = "Test";
w.DataContext = nvm;
w.Show();
}
}
What is left is to create a new MainWindowViewModel and setup a DataContext for MainWindow.
public MainWindow()
{
InitializeComponent();
var mvm = new MainWindowViewModel();
mvm.Text = "Focus me!";
DataContext = mvm;
}
Hope it will help.

wp8 Binding CheckBox to CustomMessageBox via DataTemplate

I make my own Control.
Inside that I want to define data template to use it in Custom message box.
In code I open this dialog but can't set start value to check box inside it.
Please help me - how to correctly bind cbVoiceAttChecked variable to CustomMessageBox via DataTemplate named VoiceTemplate
XAML:
<UserControl x:Class="myProj.RDPControl"
...
>
<UserControl.Resources>
<DataTemplate x:Key="VoiceTemplate" >
<StackPanel Margin="32,0,0,0">
<CheckBox x:Name="cbVoiceAtt" Content="..." IsChecked="{Binding cbVoiceAttChecked}"/>
... /*Other checkboxes*/
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
... Here is main control - works perfectly
</Grid>
In code
public partial class RDPControl : UserControl
{
public RDPControl()
{
InitializeComponent();
//this.DataContext = this;
}
public bool cbVoiceAttChecked { get; set; }
....
private void VoiceButton_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
cbVoiceAttChecked = true; // This value binding to temlate!!!
CustomMessageBox messageBox = new CustomMessageBox()
{
Caption = "...",
Message = "...",
ContentTemplate = (DataTemplate)(this.Resources["VoiceTemplate"]), // Use template from xaml
DataContext = this, // I want to use cbVoiceAttChecked variable to bind to dialog
LeftButtonContent = "yes",
RightButtonContent = "no"
};
...
messageBox.Show();
}
You need to either change your cbVoiceAttChecked property into a DependencyProperty, or implement the INotifyPropertyChanged interface in your RDPControl class.
You can find out more about the INotifyPropertyChanged Interface in the INotifyPropertyChanged Interface on MSDN and about DependencyPropertys in the DependencyProperty Class and Dependency Properties Overview pages on MSDN.
Of course, it all depends on what you are doing with the ContentTemplate object inside your RDPControl class. As you did not show that, I cannot confirm that making the above change will fix your problem.

Sharing data between usercontrol and main view

I have an UserControl(AutoComplete) with his own ViewModel. When I use the UserControl inside a window, it run well, connect to a service, and paint data correctly.
The UserControl datacontext is set via xaml, and is binded to a property of the main window viewModel.
Ok, now I want that the UserControl can load data from the main window view model. The thing is that, supposing the usercontrol loads countries. When I type in the Usercontrol it returns the list of countries and when I select one of them, i.e. "Spain", the SelectedItem property of the Usercontrols updates to "Spain". I want an object in main window viewModel to udate to "Spain" and vice-versa, if I update the country object in the main window viewmodel, the selecteditem of the user should update too.
How can I accomplish that
I have this in my mainview:
<amctrls:AmAutoCompleteView DataContext="{Binding controladorAutoCompleteCountry}" />
the user control loks like this:
<telerik:RadComboBox Margin="2,0,0,0" Grid.Row="0" Grid.Column="0"
IsEditable="True"
Name="RadCbo"
ItemsSource="{Binding objectList}"
DisplayMemberPath="{Binding fieldToShow}"
OpenDropDownOnFocus="True"
SelectedItem="{Binding selectedCountry, Mode=TwoWay}"
Text="{Binding searchText, Mode=TwoWay}"
IsTextSearchEnabled="False"
StaysOpenOnEdit="True" />
controladorAutoCompleteCountry is a property of my mainview wih is an instance of the usercontrol viewmodel.
The viewmodel of the main view manage addresses, and what I want is to bind an address country to the usercontrol in order to edit the address. If i have the usercontrol binded to an instance of its controller, how can I bind the Country object of the address?
If you need to make those 2 views independent which is good if you want to reuse your control, go with Event Aggregator or simple events. Whenever an item is selected in the user control, it will publish an event stating, something interesting has happened. Main viewmodel can subscribe to those events and do the required. A simple case would be a creating a static class with an event and RaiseEvent method, user control will RaiseEvent and main viewmodel with be subscribing the event. Data to be passed between them can be added to the event args.
It's a bit the other way around, but you can try something like this:
Have a MainView with a
combobox that is bound to the string property SelectedCountry and method ChangeCountry()
ContentControl that is bound to CountryInfoViewModel property SelectedCountryControl
You can now bind your combobox to the CountryInfoViewModel that is loaded in your MainView.
Below is an example that worked for me (note that I used caliburn micro here).
It basicly updates the CountryInfoViewModel/View when a different country has been selected.
You could improve the ChangeCountry method to get all the data, and of course improve the CountryInfoViewModel/View to show everything you want shown.
MainViewModel
class MainViewModel : Screen
{
#region fields
private BindableCollection<string> _listOfCountries;
private string _selectedCountry;
private CountryInfoViewModel _selectedCountryControl;
#endregion fields
#region properties
public BindableCollection<string> ListOfCountries
{
get
{
return new BindableCollection<string>
{
"France",
"Holland",
"Russia"
};
}
}
public string SelectedCountry
{
get { return _selectedCountry; }
set
{
_selectedCountry = value;
NotifyOfPropertyChange(() => SelectedCountry);
}
}
public CountryInfoViewModel SelectedCountryControl
{
get { return _selectedCountryControl; }
set
{
_selectedCountryControl = value;
NotifyOfPropertyChange(() => SelectedCountryControl);
}
}
#endregion properties
public MainViewModel()
{
SelectedCountry = "Holland";
ChangeCountry();
}
public void ChangeCountry()
{
SelectedCountryControl = new CountryInfoViewModel()
{
CountryName = SelectedCountry
};
}
}
MainView:
<UserControl x:Class="WpfModifyDifferentView.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<ComboBox x:Name="ChangeCountry" SelectedItem="{Binding SelectedCountry}" ItemsSource="{Binding ListOfCountries}"/>
<ContentControl x:Name="SelectedCountryControl"/>
</StackPanel>
</UserControl>
CountryInfoViewModel:
class CountryInfoViewModel : Screen
{
#region fields
private string _countryName;
#endregion fields
#region properties
public string CountryName
{
get { return _countryName; }
set
{
_countryName = value;
NotifyOfPropertyChange(() => CountryName);
}
}
#endregion properties
}
CountryInfoView:
<UserControl x:Class="WpfModifyDifferentView.Views.CountryInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Vertical">
<TextBlock Text="You have chosen the country:"/>
<TextBlock x:Name="CountryName"/>
</StackPanel>
</UserControl>

WPF User vs Custom Control

I have a grid control that I use throughout the application. I would like to extend the grid control to include a context menu with one item "freeze/unfreeze columns". If I elect to use a custom control, I cannot implement this functionality within the control -- instead, I have to implement the functionality wherever I place my custom control. The other alternative is user control, in which I can implement all the necessary functionality within the control:
<Grid>
<dxg:GridControl Name="gridData" DataSource="{Binding}" dx:DXSerializer.StoreLayoutMode="All">
<dxg:GridControl.Resources></dxg:GridControl.Resources>
<dxg:GridControl.Columns />
<dxg:GridControl.View>
<dxg:TableView ShowGroupPanel="False" MouseRightButtonUp="TableView_MouseRightButtonUp">
<dxg:TableView.ColumnMenuCustomizations>
<dxb:BarButtonItem Name="freezeColButton" Content="Freeze Column(s)" dxb:BarItemLinkActionBase.ItemLinkIndex="0" ItemClick="freezeColButton_ItemClick" />
</dxg:TableView.ColumnMenuCustomizations>
</dxg:TableView>
</dxg:GridControl.View>
</dxg:GridControl>
</Grid>
Notice, the TableView.ColumnMenuCustomization tag includes the event handler for the freeze/unfreeze functionality.
However, the only issue with the user control is that I cannot access the underlying Grid's Columns property. For example, when I place my user control (defined above) in a window, I get an error (Error 25: The tag 'ExtendedGridControl.Columns' does not exist in XML namespace 'clr-namespace:UI.Controls'):
<Window>
...
<Grid>
<uc:ExtendedGridControl x:Name="extendedGridData" >
<uc:ExtendedGridControl.Columns>
<dxg::GridColumn FieldName="FieldA" Visible="True" />
...
</uc:ExtendedGridControl.Columns>
</uc:ExtendedGridControl>
</Grid
</Window>
How can I expose the GridControl properties? Any help/suggestions would be greatly appreciated.
You need to propagate the properties by defining them on the UserControl, e.g.
public partial class Bogus : UserControl
{
// You often can reuse properties via DependencyProperty.AddOwner
public static readonly DependencyProperty ItemsSourceProperty = ItemsControl.ItemsSourceProperty.AddOwner(typeof(Bogus));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemTemplateProperty = ItemsControl.ItemTemplateProperty.AddOwner(typeof(Bogus));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public Bogus()
{
InitializeComponent();
}
}
<UserControl x:Class="Test.UserControls.Bogus" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" Name="control">
<StackPanel>
<TextBlock Text="Lorem Ipsum:" />
<ItemsControl ItemsSource="{Binding ElementName=control, Path=ItemsSource}"
ItemTemplate="{Binding ElementName=control, Path=ItemTemplate}" />
</StackPanel>
</UserControl>
The properties are visible outside and the internal controls bind to them.
For some properties you do not use a DependencyProperty, but just a clr-property which references the internal control's property, this may be preferable with certain properties that only have setters or internal constructors or are not dependency properties in the internal controls either, e.g.
public ItemCollection Items
{
get { return _itemsControl.Items; }
}

Opening multiple views by clicking button using MVVM silverlight approach

I want to use MVVM approach to achieve something like below:
I have a MainWindow where i have a 3 Buttons like: a)Customers b) Orders c) Sales
By clicking on button, it should open its respective window/usercontrol xaml with customers details,orders details,sales details.
I have tried everything but culdnt able to do so.
How to achieve this using MVVM pattern. Kindly provide the solution?
Thanks
The answer depends on how you want your Customers, Orders and Sales views displayed. If you want them displayed in the same view, simply add a content control bound to a property in your main ViewModel.
For example, if you're using the MVVM Light Toolkit, your MainPage.xaml might look like...
<UserControl x:Class="MvvmLight2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button Content="Customers" Command="{Binding DisplayView}" CommandParameter="Customers" Margin="10" />
<Button Content="Orders" Command="{Binding DisplayView}" CommandParameter="Orders" Margin="10" />
<Button Content="Sales" Command="{Binding DisplayView}" CommandParameter="Sales" Margin="10" />
</StackPanel>
<ContentControl Content="{Binding CurrentView}" IsTabStop="False" Margin="10" />
</StackPanel>
</UserControl>
And your MainPageViewModel would be...
using System.Windows.Controls;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace MvvmLight2.ViewModel
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
DisplayView = new RelayCommand<string>(DisplayViewCommandExecute);
}
#region Commands
public RelayCommand<string> DisplayView { get; private set; }
#endregion
#region CurrentView Property
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
public UserControl CurrentView
{
get { return _currentView; }
set
{
if (_currentView == value)
return;
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
#endregion
private void DisplayViewCommandExecute(string viewName)
{
switch (viewName)
{
case "Customers":
CurrentView = new CustomersView();
break;
case "Orders":
CurrentView = new OrdersView();
break;
case "Sales":
CurrentView = new SalesView();
break;
}
}
}
}
This all assumes that you have created views and view models for Customers, Orders, and Sales, and modified the ViewModelLocator to include them.
At this point, if you need to display specific information in your child views, you can create a dependency property in them, and set that from your MainViewModel before you display the view.
You may want to look into the mediator pattern . Common implementations are the Messenger class in the MVVM Light Toolkit and Event Aggregation in PRISM.
One basic workflow using this pattern... Command is called on viewmodel1. Viewmodel1 registers some message with the mediator. Viewmodel2 subscribes to that message and does something in response (like creates new view2 or changes visual state of the view2).
I tried this using Sliverlight Naviagtion Application and MVVM
http://garfoot.com/blog/2010/09/silverlight-navigation-with-the-mvvm-pattern/
Pretty simple example. No frameworks involved as such.
But using a MVVM framework makes life easier for future use.
For MVVM and Prism framework check this link..
http://blog.roboblob.com/2010/10/24/introducing-prism-navigation-framework-for-silverlight-mvvm-applications/

Resources