In my project I have a Window called AccountWindow.xaml which has a ContentControl to display the two UserControls.
AccountWindow
<Window>
<Window.Resources>
<!-- Login User Control Template -->
<DataTemplate x:Name="LoginUserControl" DataType="{x:Type ViewModels:LoginViewModel}">
<AccountViews:LoginUserControl DataContext="{Binding}"/>
</DataTemplate>
<!-- Registration User Control Template -->
<DataTemplate x:Name="RegistrationUserControl" DataType="{x:Type ViewModels:RegistrationViewModel}">
<AccountViews:RegistrationUserControl DataContext="{Binding}" />
</DataTemplate>
</Window.Resources>
<Grid>
<!-- ContentControl that displays the two User Controls -->
<ContentControl Content="{Binding}" />
</Grid>
</Window>
I then have two user controls called LoginUserControl and RegistrationUserControl
Login User Control
<Grid Background="Pink">
<Button Content="Switch To Register View" Command="{Binding SwitchToReg}" Margin="100" />
</Grid>
Register User Control
<Grid Background="Orange">
<Button Content="Press Me" Command="{Binding PressMe}" Margin="100" />
</Grid>
Both the Login User Control and the Registration User Control have their own ViewModels with a RelayCommand inside that is bound to the buttons as shown in the code.
Login View Model
public class LoginViewModel
{
public RelayCommand SwitchToReg
{
get
{
return new RelayCommand(param =>
{
Console.WriteLine("Switch To Reg");
// Somehow change the content control in the AccountWindow to show the RegistrationDataTemplate???
});
}
}
}
The Problem
I want to be able to change the content of the ContentControl in the AccountWindow when the user presses on one of the buttons in the UserControls. For example, when the user presses the button in the Login User Control called "Switch To Register View" it executes a the Command SwitchToReg and changes the content control to RegistrationUserControl & its ViewModel. How could this be possible?
To achieve that you would need to pass a reference for the AccountWindow into the UserControl when you are constructing it and then your Command can update the ContentControl using the reference you have provided.
This is introducing coupling which is better to avoid so instead I would suggest thinking about the design of the AccountWindow. I would use grid rows to separate the ContentControl area from the button which will change the UserControl.
In the window above, the blue area is where I would host the ContentControl and the red area is part of the AccountWindow.
This way, the behaviour for switching the ContentControl is entirely handled by the AccountWindow.
You can create a property and attache it to the control.
Or you can create another user control and make it visible o not controled by the property that you created.
Related
I am relatively new to WPF and I'm trying to create a system where you can click a button to add a new tab which generates a corresponding grid to display content. For example, when you open a new tab, the first page would be "Home page", you then have options within this new tab to navigate to other content such as "Options". You can then add another tab and repeat the process etc. IT would be like Google Chrome how each tab represents another browser experience.
So far I have my tabs working, however I am stuck with how I am supposed to generate grids for each tab. Does anyone have any suggestions on how I could do this?
Create an abstract TabData base class with a Header string property.
Create a class that derives from TabData for each type of tab that you want (e.g. HomeData, OptionsData, etc.)
Create a view model with an Items property of type IEnumerable<TabData>.
Bind the ItemsSource property of the TabControl to Items and define an implicit DataTemplate for each concrete TabData type:
<TabControl ItemsSource="{Binding Items}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.Resources>
<DataTemplate DataType="{x:Type local:HomeData}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type local:OptionsData}">
...
</DataTemplate>
</TabControl.Resources>
</TabControl>
The DataTemplate defines the appearance of a tab and may contain a DataGrid or any other element.
I am working on a WPF application which is developed using MVVM pattern. The MainWindow has several Usercontrols that open when an action is performed. However, I want to close the Usercontrol once the actions are complete and on Clicking a button. I have looked in several places, but haven't had any luck with it so far. Any help would be appreciated.
It is being pointed out that my question is a duplicate of this :
Close View from ViewModel
But it is actually not, since that thread talks about closing a Window, mine is about closing an UserControl.
Adding some code to make it clear:
This is the ItemsControl in the first UserControl which hosts the second Usercontrol:
<Grid x:Name="UserControlGrid" Width="Auto" Height="auto" Margin="0,0,0,0">
<ItemsControl ItemsSource="{Binding ViewsToShow}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True" Width="auto" Height="auto"></Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
Now to open the second UserControl , in the first UserControl ViewModel, I do this:
private ObservableCollection<ObservableObject> viewsToShow = new ObservableCollection<ObservableObject>();
public ObservableCollection<ObservableObject> ViewsToShow
{
get
{
return viewsToShow;
}
set
{
viewsToShow = value;
OnPropertyChanged("ViewsToShow");
}
}
ViewsToShow.Add(new SecondUserControlViewModel());
Thank you
The answer is: you should not close your usercontrols (unless they're used as separate dialogs, and this is not your case, according to your comment above).
All changes in usercontrols visibility are about navigation. Once you logically navigate to a functionality involving another usercontrol, you have to hide old one and show new control. Usually this is done via template selection:
Two templates, one per UserControl, each associated with respective ViewModel:
<DataTemplate DataType="{x:Type ViewModels:FirstViewModel}">
<controls:FirstControl />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:SecondViewModel}">
<controls:SecondControl />
</DataTemplate>
Then later we declare a placeholder:
<ContentControl Content="{Binding ViewModelSelector}" />
Once ViewModelSelector property returns FirstViewModel, our placeholder will show FirstControl. If we navigate ViewModelSelector property to SecondViewModel, our placeholder would auto-replace FirstControl with SecondControl and vice versa.
I'm wondering how to go about creating different views in the main window when a button is pressed. I'm not sure of the correct terminology, so that has hampered my google fu.
I'm thinking that the main viewing area would be a content control, that I could change when a event happens. I made a small drawing to help illustrate my idea / thought.
Any input will be appreciated. Thanks!
It would be really easy to implement this senario using MVVM approach....
Make a ViewModel for you MainView. Then Define Properties of the ViewModels of your UserControls
For Example You have Two UserControl as FirstView and SecondView then make a properties in your viewmodels as ViewToLoadProperty of the type ViewModel (usually called as ViewModelBase)
Set bindings as
<!-- Panel For Hosting UserControls -->
<Border Grid.Column="2">
<ContentControl Name="userControlContentControl"
Content="{Binding Path=ViewToLoadProperty,
}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type ViewModelLayer:FirstViewModel}">
<ViewLayer:FirstView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModelLayer:SecondViewModel}">
<ViewLayer:SecondView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Border>
<!-- Panel For Hosting UserControls -->
Then when you click the button Use a command to set the respective ViewModel Intance to this(ViewToLoadProperty) property...(Use RelayCommannds or something like it)
DataTempates would do the rest of the job by selecting the right View according to the right type of ViewModel
YOu can use MVVMLight toolkit if you are implementing MVVM Pattern.. :)
On the right you could have a frame. Then the button would bind a different page or user control to the content of that frame.
I have created a tab control and Created the tabItems dynamically, but i dont know how to add controls into the tabItems using MVVM. Could any one help me
There are a few ways to programmatically add Tab Items in WPF and I am going to show you a simple example on how I deal with this in my application.
First I host a collection of the ViewModels for the TabItems (or Workspaces as I refer to them) in my MainWindowViewModel.cs:
private ObservableCollection<WorkspaceViewModel> _workspaces;
public ObservableCollection<WorkspaceViewModel> Workspaces
{
get
{
if (_workspaces == null)
{
_workspaces = new ObservableCollection<WorkspaceViewModel>();
}
return _workspaces;
}
}
Next I add a reference to the various controls in my MainWindow.xaml. This is important as we want to make sure that whenever the collection contains a ViewModel that it displays the appropriate View for that Model.
<Window.Resources>
<DataTemplate DataType="{x:Type vm:MyUserControlViewModel}">
<vw:MyUserControlView/>
</DataTemplate>
</Window.Resources>
If you have multiple types of UserControls you simply add them all here like this:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:FirstUserControlViewModel}">
<vw:FirstUserControlView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SecondUserControlViewModel}">
<vw:SecondUserControlView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ThirdUserControlViewModel}">
<vw:ThirdUserControlView/>
</DataTemplate>
</Window.Resources>
Next we add the TabControl and bind it to our Workspace Collection.
<TabControl ItemsSource="{Binding Workspaces}"/>
Then I simply add my ViewModels to the Collection to have them show up in the TabControl.
Workspaces.Add(new FirstUserControlViewModel());
Workspaces.Add(new SecondUserControlViewModel());
Workspaces.Add(new ThirdUserControlViewModel());
My WorkspaceViewModel that I base the TabItem collection of is very simple and looks something like this:
public abstract class WorkspaceViewModel : BaseViewModel
{
public String HeaderText { get; set; }
public override string ToString()
{
return HeaderText;
}
}
Adding a TabItem:
To create a TabItem you simply create a UserControl and ViewModel like you normally would using WPF and the MVVM pattern.
namespace MyApplication.ViewModel
{
public class FirstUserControlViewModel : WorkspaceViewModel
{
public FirstUserControlViewModel ()
{
base.HeaderText = "My First Tab";
}
}
}
Next you need to bind a View to your new ViewModel.
<DataTemplate DataType="{x:Type vm:FirstUserControlViewModel }">
<vw:FirstUserControlView/>
</DataTemplate>
Then you create an instance of the ViewModel and add it to the collection in your MainWindowViewModel.
FirstUserControlViewModel firstvm = new FirstUserControlViewModel();
Workspaces.Add(firstvm);
And now the TabItem should show up in your TabControl.
Loading TabItems dynamically using Extensions:
In some cases you might even need to load TabItems from plugins dynamically without the host application first knowing about the TabItem. In these cases you need to have the plugin register the View and ViewModel with the application domain.
This is very easy to do, and actually something I do for one of my MEF based projects. I have an post here, with some additional details as well.
All you need to do is add a Resource Dictionary to your plugin/extension and make sure that the host application loads it once the plugin has been imported.
To show you a fast example I would have a View.xaml in my extensions:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:MyExtension.Test">
<DataTemplate DataType="{x:Type vw:TestViewModel}">
<vw:TestView/>
</DataTemplate>
</ResourceDictionary>
I then expose the ResourceDictinary using MEF to the Host like this:
private ResourceDictionary _viewDictionary = new ResourceDictionary();
public ResourceDictionary Dict
{
get
{
return _viewDictionary;
}
}
_viewDictionary.Source =
new Uri("/MyExtension.Test;component/View.xaml",
UriKind.RelativeOrAbsolute);
Last you use Application.Current.Resources.MergedDictionaries.Add to load the View.xaml into the host.
You Dont have to add controls you just have to specify the UserControl.
TabControl has two properties ItemTemplate && Content Template
ItemTemplate is for how the Tab will look where as
ContentTemplate is how the Tab Content will Look... so...
Xaml for the above
<TabControl Grid.Row="1"
ItemsSource="{Binding Path=TabList}"
SelectedItem="{Binding Path=SelectedTab,
Mode=TwoWay}"
<!--This is How tab will look--> >
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="20"
Height="20"
Margin="0,0,2,0"
Source="Images\TreeView\yourFavImg.png" />
<TextBlock Margin="0,4,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=TabText}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!--This will be the content for the tab control-->
<TabControl.ContentTemplate>
<DataTemplate>
<!--This User Control will contain the controls you like-->
<ViewLayer:YourFavUserControl />
</DataTemplate>
</TabControl.ContentTemplate>
you dont have to add controls if you use mvvm. you just have to create datatemplates for your viewmodel objects you wanna display.
all you need is a contentcontrol/presenter which is bind to your viewmodel and the datatemplate will show what you want.
What would be the best way to build a data-navigation like in access-forms in XAML/C#?
Should I build a user control (or even custom control) that I just bind to my collection in which I can put other controls? (hence this question: C# User Control that can contain other Controls (when using it) )
Or can I build something by deriving from then ItemsControl somehow? how?
Or would this be done completely different today (like "this style of navigation is so last year!")?
I'm relatively new to C# and all (not programming as such, but with more like "housewife-language" Access-VBA) also I'm no native english speaker. So pls be gentle =)
You can create user control and place a bunch of buttons (First, Prev, Next, Last, etc..) in it and place it on the main window. Secondly, you can bind your data navigation user control to a CollectionViewSource which will help you to navigate among your data.
Your main window:
<Window.Resources>
<CollectionViewSource x:Key="items" Source="{Binding}" />
</Window.Resources>
<Grid>
<WpfApplication1:DataNavigation DataContext="{Binding Source={StaticResource items}}" />
<StackPanel>
<TextBox Text="{Binding Source={StaticResource items},Path=Name}" />
</StackPanel>
</Grid>
Your Data Navigation User Control:
<StackPanel>
<Button x:Name="Prev" Click="Prev_Click"><</Button>
<Button x:Name="Next" Click="Next_Click">></Button>
<!-- and so on -->
</StackPanel>
And your click handlers goes like this:
private void Prev_Click(object sender, RoutedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(DataContext);
if (view != null)
{
view.MoveCurrentToPrevious();
}
}
I hope this helps.
Sounds like you're after a DataGrid control. Microsoft is releasing a WPF DataGrid as part of a WPF Toolkit which you can download here: http://wpf.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=25047.