Split pages and navigate in WPF - wpf

I'm trying to create a WPF page project, in which I've split the screen in two. On the left side, I have four clickable links / buttons.
Clicking on one of the links opens a corresponding page on the right side of the screen. There, options can be set. When a user uses the navigation bar on top of the screen, it should apply on the right side only.
Is this possible to do? What would be a better approach?
I'd like to know how to tell a page (part) which page to load. So that would make the right page dynamic?
Would it be better to split up the Grid? Or would a DockPanel be a better solution?
I've created a large Window in WPF, but I'd like to split up all these pages in a usefull navigation Page. So I have a bit of experience in using WPF and XAML. How should I approach this problem?

Firt you need to identify the components you want, mainly I see three components, the LeftSide navigation pane, the TopSide navigation pane and the MainContent pane. First lets talk about the MainContent pane, I think the best way for doing this is to use binding and date templates for making this dynamically. In your ViewModel, or DataContext, you need to have a property that represent the Content that you want to show in the MainContent, lets call it MainContent, then the MainContent View could be a ContentControl and set the property Content bindings to the ViewModel's MainContent property. In this way you only need to set the DataTemplate for each class item that you want to show. Other way could be to use a tab control and chage the ControlTemplate, this way is not dynamic because you need predefine all contents that you will show.
Now, for the navigation pane, you could use any control, for instance you could use a Radio button and change the ControlTemplate, and make the logic in the view model, using commands, for instance.
And now, the use of the Grid or DockPanel depends of what you want your application to do. If you want a dynamic width, you should to use a Grid with a GridSplitter, but if you want fixed width, you could to use a DockPanel due it is a bit more efficient/faster than the Grid.
Hope this answers helps, and not to be confused. Please feedback if any doubt.
EDIT
For instance, in the view model:
public class MainViewModel:INotifyPropertyChanged
{
public object Content {get; set;} //Here you must to raise the PropertyChanged event
private ICommand _showSummaryCommand
public ICommand ShowSummaryCommand
{
get { return new _showSummaryCommand ?? (_showSummaryCommand = new SomeCommandImplementation((sender,e)=>{Content = new SummaryViewModel()},true))} //most of commands implementations recive two parameters, an delegate to execute and a bool delegate for can excecute
}
}
and for the view, in some resource dictionary:
<DataTemplate TargetType="{x:Type ViewModels:SummaryViewModel}">
<DataGrid>
<!--Here goes what ever you want to show for instace-->
<TextBlock x:Name="descriptionText" Text={Binding Description}/>
</DataGrid>
</DataTemplate>
and in the place where you will to show all the contents
<!--....-->
<ContentControl Content={Binding Content}/>
<!--....-->
Hope this code helps a bit more :)

Related

WPF multiple views [duplicate]

I am working on a an WPF MVVM application where I need to have a Main Window with just a logo and it has to show child views inside it. I don't have any controls in Main Window all the controls reside in child view for example Buttons like Next, Back, Cancel and some text blocks etc. Now If users select Next button on the child view I have to draw or load the next child view inside the Main Window. If Back button is clicked I have to go back to the previous child view. So basically I am changing the child views depending on which button is clicked. Also I am maintaining different view models for every child view. Now the problem is I am not able to figure how should I link the child views to there respective view models. This application is similar to some Installation applications where different dialogs are shown depending on the selection and the button clicked by the user.I am new to this wpf and don't want to use MVVM Light , Prism etc. Any detailed help will be greatly appreciated. Thanks in advance.
One of the easiest ways to associate any data type with XAML controls is to use a DataTemplate. Therefore, you can simply add something like this into your Application.Resources and as long as you do not set the x:Key properties on the DataTemplates, then they will be explicitly applied by the Framework whenever it comes across instances of your view models:
<DataTemplate DataType="{x:Type ViewModels:HomeViewModel}">
<Views:HomeView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
Then displaying the view is as simple as this:
<ContentControl Content="{Binding YourViewModelProperty"} />
In code behind, or your view model:
YourViewModelProperty = new MainViewModel();
It's often handy to create a base class for your view models and then the YourViewModelProperty can of that type and you will be able to interchange them using the same property and ContentControl.
UPDATE >>>
The general idea is that you have one MainViewModel class with one BaseViewModel property data bound to one ContentControl in MainWindow.xaml... the navigation controls should also be in MainWindow.xaml and not in the views themselves. In this way, the MainViewModel class is responsible for changing the property to the relevant view model instances when it receives navigation Commands from the MainWindow.xaml.

Multiple windows in WPF and the ability to tile them

I am writing a terminal in WPF, which communicates the host with an embedded device through RS232.
I want to be able to open multiple connections that will reside on different tabs, I believe that for that purpose WPF's tabContorl is sufficient, However the customer wants to be able to tile the different tabs on the screen, and as I understand the basic tabControl doesn't allow you to do that.
Any ideas how can this be done?
any help will be appreciated,
Thanks in advance.
Maybe it's overkill, but I would give a try to Avalon Dock from the WPF Toolkit, it's free. With that, you will be able to move terminals around, dock them wherever you wish and even have only opened at a time if you unpin others.
I have made a custom WPF control called ModalContentPresenter that allows you to display modal content which I think will be suitable. The control allows you to conditionally display additional content on top of your primary content.
Your application window will comprise a single ModalContentPresenter which will contain your TabControl in it's primary content and an ItemsControl in it's modal content.
<c:ModalContentPresenter IsModal="{Binding IsTiling}">
<DockPanel>
<Button Content="Show tiled view"
Command={Binding ShowTiledViewCommand}
DockPanel.Dock="Top"/>
<TabControl ItemsSource="{Binding Connections}"> />
</DockPanel>
<c:ModalContentPresenter.ModalContent>
<DockPanel>
<Button Content="Hide tiled view"
Command={Binding HideTiledViewCommand}
DockPanel.Dock="Top"/>
<Itemscontrol ItemsSource="{Binding Connections}" />
</DockPanel>
</c:ModalContentPresenter.ModalContent>
</c:ModalContentPresenter>
By default the modal content will not be displayed so all the user will see is the TabControl.
Both the TabControl and ItemsControl are bound to the same collection of data in your viewModel ensuring that the tiled view is in sync with the items in the tab control.
Your viewModel must have a Boolean property called IsTiling which should return true when you want the 'tiled' view to be shown and false when it is hidden.
When the tiled view is displayed users will be unable to interact with the primary content.
You can change the layout panel used by the ItemsControl to change how the collection of data is 'tiled'.
See this answer for an additional example of how to use the control.
The interface of your viewModel will look something like this:
public interface ViewModel {
public ObservableCollection<Connection> Connections { get; }
public boolean IsTiling { get; }
public ICommand ShowTiledViewCommand { get; }
public ICommand HideTiledViewCommand { get; }
}
Once you have this working you can add some additional enhancements to improve the look and feel of the user interface.
I would start by assigning a custom control template to the TabControl so that a Button is displayed in the 'header' area. This Button is bound to a command in your viewModel which is responsible for changing the IsTiling property to true.
This question provides a link to an answer which explores ways of achieving this.
A second enhancement is to remove the button from the modal content and call the HideTiledViewCommand command when the user selects an item in the items control. You can then add some additional logic which selects the correct tab when the tiled view is closed. I think this can be achieved by having an additional property in your viewModel representing the selected connection.

WPF MVVM Default Focus on Textbox and selectAll

I have situation like this, I have 2 WPF forms developed using MVVM Pattern ..2nd form will be opened from first(form1 will be in backend till form2 is closed) and closing the second makes the first form active.
Now I want to make a textbox on form1 with default focus set on it. I was able to do it with FocusManager and its working fine but the same is not working fine when Im getting into form1 from form2. Also during this time I have to set the focus on the default textbox and also I need to select all the text present on it. I am unable to understand how to do this with viewmodel.
Any suggestions will be of great help for me.
Regards,
Krishna
You can focus a particular UI element using the FocusManager.FocusedElement Attached Property:
<Grid FocusManager.FocusedElement="{Binding ElementName=SomeTextBox}">
<TextBox Name="SomeTextBox" Text="{Binding SomeProperty}" />
</Grid>
This should select the TextBox each time the view/UserControl is loaded.
As for how to select text from the view model... the solution would be the same to handle any event when using MVVM. Wrap it in an Attached Property. Please beware that it is not appropriate to handle all events in the view model, as it should not really have any knowledge of purely UI events. However, the choice is yours.
To 'wrap', or handle any event in an Attached Property, you basically declare a class that extends the DependencyObject class and define one or more static properties. Rather than go over the whole story once again, I'd prefer to direct you to my answer to the What's the best way to pass event to ViewModel? question on Stack Overflow, which provides further links and a full code example.
For background information on Attached Properties, please see the Attached Properties Overview page on MSDN.

Bind border style to current user control

I`m developing a wizard application which has a side menu with 5 borders and a content control that contain application screens (user controls).
The borders styles are suppose to give to user an indication where he is at the wizard steps.
I wrote 2 border styles - the first one is the defult style which applied on all borders by default.
The second one (isFoucusedStyle) need to be applied by the border that suitable to the current screen.
For example when the wizard is showing the first screen: first border need to use the isFoucusedStyle and the others need to use defult style. When the user continues to next screen, the first border need get back to default style and the second border now will apply isFoucusedStyle.
I create the pages instances via xaml at the mainWindow under resources at the next way:
xmlns:view="clr-namespace:App.View"
xmlns:ViewModel="clr-namespace:App.ViewModel"
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:OpeningViewModel}">
<view:OpeningView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:PersonalDataViewModel}">
<view:PersonalDataView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:BusinessDataViewModel}">
<view:BusinessDataView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:BusinessDataViewModel}">
<view:BusinessDataView/>
</DataTemplate>
I also have a property - CurrentPage which binded to ContentControl - when the user clicks "next page button" CurrentPage updates and the ContentControl switch UserControl.
There is no any binding between the borders to User Controls, in my current state the borders are just visual graphics without any features.
How can i implement it?
Thanks
I took the "isFoucusedStyle" and config it to baseOn defultstyle.
I added triger to isFoucusedStyle which turns on when Border.Focusable is true.
I Created a converter which has access to current page number.
At every border I bounded focusable property to the converter and sent it "converter parameter" with the suitible page number (page number which represented by current border)
The converter is checking for equility between currentPageNumber to converterParameter and returns boolean result.
The result is turning on (or not) the trigger and set the needed border style.
Thanks anyway.
Firstly I would strongly suggest you base your wizard on the NavigationWindow (or containing a NavigationFrame), this will give you all your back and forwards navigation for free, and if you want you can always re-style your NavigationWindow to match a more wizard like interface (see WPF Wizards). NavigationWindow/Frame also supply you with Navigate() methods that handle the transition between pages.
In order to handle the navigation links (your five side menu items) I bind each link to a View level ICommand which tests to see if we need are already on the correct page in CanExecute. Setting ths borders is then just a case of {Binding CanExecute, Converter={BoolToColorConverter}}.
In your case, you can simply do the same thing. Setup your command to check if we have the right CurrentPage, and Bind as above using a Converter.

PRISM + Tabs = Pain

I am having difficulty trying to get a very simple scenario working with PRISM 2.0 for WPF. I want the main workarea of my application to be a TabControl. Each time I add a view, I want it to appear as a TabItem on the TabControl.
Sounds easy right?
My region, which is in my Shell.XAML looks like this:
<Controls:TabControl
Name="MainRegion"
cal:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.TabRegion}"
ItemContainerStyle="{StaticResource ShellTabItemStyle}" />
The style: ShellTabItemStyle looks like this:
<Style x:Key="ShellTabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Content.DataContext.HeaderInfo, RelativeSource={RelativeSource Self}}" />
</Style>
This should set the Header of the TabItem to the HeaderInfo property on the DataContext of the view. (I got this idea from this article) The DataContext of my view is a very simple Presenter which has a HeaderInfo property on it:
public string HeaderInfo = "The Header Text";
My view is a simple WPF usercontrol and looks like this:
<StackPanel>
<TextBox Text="Hello World" Name="MyTextBox"></TextBox>
<Image Source="..SomeImage.PNG" Name="MyImage"></Image>
</StackPanel>
So far, so good. If I add the view to the region I get a tab control and I get a tab with the text set to "The Header Text". My problem is that there is absolutely no content appearing on the tab. My view contains a simple Image and a TextBox, but neither of them show up in the TabItem. If I break out Snoop and look around, there isn't an image in sight.
What am I missing here - is there an easier way?
I was unable to get any of the suggested answers to work. Extensive googling didn't help either. I gave the problem some thought over the weekend and more I thought about it, the more it occured to me that there is a bit of a code smell about this approach. You inject a view into your Tab Region... some magic stuff happens and a tab gets added... you have to add some imcomprehensible dynamic binding to some XAML styling stored in a file somewhere and this may or may not set your header text. If any one single element of this is just a little bit wrong you won't get an error but it just won't work.
In my view this is both brittle (i.e. very easy to break) and pretty inpenetrable unless you have a deep understanding PRISM, the model, and of XAML. Fortunately there is a much nicer and simpler way to do this:
Simply create a view called TabRegionView which contains only a blank TabControl. You probably want to add this to your Shell.xaml. Create an Event called InjectTabView which has a Payload of type UserControl and subscribe to this event in your TabRegionView control. When the event fires in TabRegionView, you create the TabItem manually and add the view to the TabItem like so:
public void TabAdded(UserControl view)
{
var item = new TabItem();
item.Content = view;
item.IsSelected = true;
item.Header = "Header Text";
mainTab.Items.Add(item);
}
When you want to display a view as a new tab, your code looks something like this:
var view = new View(params);
_eventAggregator.GetEvent<InjectTabViewEvent>()
.Publish(view);
This will be picked up by TabRegionView and the view will be added as a new tab. You could easily wrap the View in harness of some type that contains header text, an image and bool to indicate whether or not the tab should be autoselected.
IMHO this technique has the dual advantages of giving you direct control of what is going on AND it is much easier to follow.
I'd be very interested to get an opinion on this from any PRISM officianados.
If you are using RegionManager, you need to Activate your view. There's likely some piece of code where you are adding the view to the region, you just additionally need to tell that region to activate it.
public void AddViewToRegion(IRegion region, object view)
{
region.Add(view);
region.Activate(view);
}
It seems silly, but you get the behavior you are seeing if you don't do this. I found this kinda frustrating, but it was easy enough to fix. The behavior is even stranger when you add multiple tabs if you don't at least Activate the first view you add.
My understanding is that if you don't do this, the view never becomes part of the visual tree (this is a side-effect of the fact that the TabControl deactivates (removes from the visual tree) when a tab isn't "in front". This is good for certain operations, but makes things like this be a little wonky.
Some random thoughts:
try to remove the style from TabControl
check visual tree with help of Snoop tool. You should see your TabItem with view's UserControl under the TabControl. Next you can check what's wrong with that UserControl and its children (your view's content). They are probably hidden for some reason.
other thing to think over - RegionAdapter. RegionAdapters are responsible for adapting regions' views to host UIControl.

Resources