Which WPF layoutmanager or other control do you recommend? - wpf

I'm quite new to WPF and have to create an application. I have a window (MainWindow), with a main menu at the top of it with 5 menu items.
I don't want to create new pop-up windows for each menu items but I'd like my program to respond the menuitems and so displays other and other surfaces inside this MainWindow (the menu will be statically at the same position).
I found UserControl and this will be a solution if you don't suggest anything else. Might be easy question for you, but also a big help for me. Thank you.

all you need is a toolbar with toolbaritem for your menu and a contentcontrol where you set the content property to the content in dependence of your menu item. and if you do a little bit MVVM you simply set the vm to the content property and define some datatemplates for your views.
<Window>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Height="auto" />
<ColumnDefinition Height="*" />
</Grid.ColumnDefinitions>
<ToolBar Grid.Column="0">
</ToolBar>
<ContentControl Grid.Column="1" Content="{Binding Path=ActualWorkspace}" />
</Grid>
</Window>

Related

WPF tab control with grid and shared content

I have a wpf tab control in which I would like to have two columns. One column would always show a graph control that will be used to display data depending on the tab selected - but it will always be the same graph control.
Unfortunately, due to the design of the graph control, it is not possible to have more than one graph control mainly because the performance is dismal. I have tried that and it does not work properly.
The other column would show items like combo boxes, radio buttons, etc., that are specific to the selected tab - an example is below
I have also had the tab control in the right-hand column, but the layout of the individual tabs is congested in the right-hand column making for a less than ideal user experience.
Right now, I have the tab control hosted in a grid that has two columns with the column span set to two. For the right-hand pane, I have various group boxes and I control the visibility of those group boxes with triggers using the IsSelected property of the corresponding tab item. This, however, is causing other problems that I have traced to the visibility of the problematic controls.
What I would like to do is modify the template of the control so that I can host all the present controls within the tab control so that the graph control always displays on the left, and that the content of the right-hand tab is controlled by the selected tab.
I figure that doing this will involve either the control template or another template for the tab control, however, I have been unable to find anything like this so far. Is there a way to do something like this and if so, is there a guide to doing so or some hints as to how I might accomplish this?
Thanks.
The way I would approach this requirement would be something like below. And I would apply a template/style to buttons so that they have a look of a TabHeader.
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<StackPanel.Resources>
<ControlTemplate x:Key="ButtonTemplate">
<!--Template style-->
</ControlTemplate>
</StackPanel.Resources>
<Button>Root bending</Button>
<Button>S-N curve bending</Button>
<Button>S-N curve contact</Button>
</StackPanel>
<Grid Grid.Row="1" Grid.Column="0">
<!--Your graph control goes here-->
</Grid>
<Grid Grid.Row="1" Grid.Column="1">
<!--Show/hide these based on buttons-->
<!--Control 1 with combo boxes, radio buttons, etc.-->
<!--Control 2 with combo boxes, radio buttons, etc.-->
<!--Control 3 with combo boxes, radio buttons, etc.-->
</Grid>
</Grid>
you can declare ChartControl as a Resource and use it in every Tab.
To confirm that ChartControl is the same, type something in TextBox and then select another Tab. Text stays the same. Initialization time shown in TextBlock stays the same.
<Window x:Class="XamlApp.Window6"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="TabsWindow"
Height="480" Width="640">
<Window.Resources>
<Grid x:Key="IamChartControl" Background="Khaki">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}}" Margin="5"/>
<TextBox Text="hello" Grid.Row="1" Margin="5"/>
</Grid>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding Source='12345'}">
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl Content="{StaticResource IamChartControl}"/>
<TextBlock Grid.Column="1" Text="{Binding}"/>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Thanks for the suggestions. Given there is a large amount of existing code and since the graph control is not entirely WPF MVVM compliant, the better answer in this case would be the answer posted by Ritesh. Putting the chart in a resource would have required more of a rewrite of the code than I presently have time to do.
However, I figured out the problem that I was seeing - which was some controls were not showing bold text when I thought that they should be. This was entirely my fault.
On each tab for each field, I had multiple different labels that were made visible depending on the result set that the user chooses. It has been a long time since I visited this code, and what I was doing was adding the bold fontweight because it makes the values stand out better.
Embarrassingly, I forgot that I had implemented it this way.
Instead of the multiple different labels approach, I am going use a single label for each field and set the appropriate content binding in a multidata trigger as that will make the somewhat cleaner. Its a pretty complicated app.
I was going to delete this, but others have asked a similar question, however, I think Ritesh's answer is different than the other cases.

Wpf application with a fixed panel

I need to create a Wpf application with this type of navigation:
A main screen with a fixed top panel, and the rest of the window that acts as a container for other views. The container can be populated with 0 or 1 views - there's never a situation when 2 or more windows should be seen at the time in this container.
I've added this very childish drawing I made:
The top green panel is the navigation panel, and when pressing "A" button, I need viewA to be opened in the red part of the window.
Similarly, when pressing "B" button, I need viewB to be opened in the red part of the window instead of viewA.
My questions:
What objects should I use to represent viewA, viewB and the containing window? Are these Windows? Pages?
What is the best way of switching between viewA and viewB? I prefer avoiding loading both viewA and viewB (and any other view I'll have) into memory and just hide\show them..
Thanks.
You could put all the "Green" stuff in directly in your window and "Red" stuff (UserControl likely) inside a ContentControl and then bind the Content property of that to the red's stuff. So it'd be like this:
<Window x:Class="garbage.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!--Green stuff-->
<Button Content="A" />
<Button Content="B" Grid.Column="1"/>
<ContentControl Grid.Row="1" Grid.ColumnSpan="2" Content="{Binding RedStuff}">
</ContentControl>
</Grid>

WPF Control, UserControl, Template confusion

Disclosure:
I am new to WPF, about a week into it.
Problem:
I am trying to modify the behavior of a GridSplitter, to make it snap to interesting positions, to show a label (that follows the splitter) with current position, to have a context menu driven from said label, etc. I have prototyped all of this successfully on one gridsplitter in one simple test application, with a combination of XAML and some code behind.
Of note is that because the GridSplitter can't host content, I placed the label in the same grid cell as the splitter so that they move together.
So far so good....
Now I wish to replicate my work so that I can use my new GridSplitter functionality in place of the native control in many locations, and furthermore, I wish to have two variants, a horizontal and a vertical. Sounds like inheritance...create a subclass derived from GridSplitter and add in the additional functionality. But all of the reading I have done leaves me wondering how to go about this, and if this is even possible without starting over again and building my own GridSplitter from scratch?
Ideas welcome. Until then I will resume the fetal position.
Thanks
This answer might help you resolve your issue: How to make GridSplitter to "snap" into another element?
By subscribing to the GridSplitterDragCompleted event, you can insert your logic to snap to "interesting" positions.
You should
create a new control derived from GridSplitter.
subscribe to DragCompleted event to implement snapping functionality like DLeh mentioned.
add a few new properties for Label , ContextMenu etc.
supply a style for your new control.
This answers how to place content in the splitter
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Row 0" Background="Orange"/>
<!--<GridSplitter Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" Height="20" Background="Purple"/>-->
<GridSplitter Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<GridSplitter.Template>
<ControlTemplate TargetType="{x:Type GridSplitter}">
<TextBlock Text="TextBlock splitter" Background="Yellow" FontWeight="Bold"/>
</ControlTemplate>
</GridSplitter.Template>
</GridSplitter>
<Button Grid.Row="2" Content="Row 0" Background="Salmon"/>
</Grid>

TabControl width increases on adding tabs

My application UI is divided in 2 parts. Left side is Navigation Menu and Right is View Area where the selected Menu content is displayed. Now, one of the menu is reports. I am using Tabcontrol with Header and Content Template. Template has a ViewModel as DataType and content as the respective View which is a UserControl. This TabControl is inside a scrollviewer which is set as horizontal and vertical alignment to stretch.
The user control hosts a ContentPresenter inside a Grid which is bound to a ReportHost which has a reportviewer as child. I am using this ReportViewer to generate reports.
When the user opens a report, it opens in a new tab. It works fine till the number of tabs is such that the tabheaders are contained inside the viewing area. But as soon as more tabs are added, it causes the tabcontrol width to stretch, causing the content area of the tab to stretch and the contentpresenter also stretches causing horizontal scroll to appear.
This finally result in the report to stretch and due to some reason, unknown to me, the report overlaps the Navigation Area of the UI, as if it is not a part of the UI but is overlapping it. The whole report keeps on floating on top of the View Area and Navigation menu on scrolling.
I can fix it by providing the MaxWidth to the ScrollViewer but I don't want to do that. I would like the width of the tabcontroll or the Scrolviewer to be decided purely based on available View Area. How do I do this through the code or XAML without using fixed width.
I am not sure if I was able to explain the situation. Please let me know if more information or clarification is needed. I would be more than happy to provide details.
Edit: Adding Code for information.
<DataTemplate x:Key="TabContent" DataType="{x:Type VM:ReportViewModel}">
<View:Report/>
</DataTemplate>
<DataTemplate x:Key="TabHeader" DataType="{x:Type VM:ReportViewModel}">
<ContentPresenter Content="{Binding Path=TabHeader}"
VerticalAlignment="Center"/>
</DataTemplate>
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap"
Text="Reports" VerticalAlignment="Top" Margin="10,13,0,0"
FontSize="18.667" FontFamily="Segoe UI" Foreground="White"/>
<Border BorderThickness="0" Margin="0,50,0,0"
Background="{DynamicResource Brush_HeaderNew}" Height="50" Width="Auto"
VerticalAlignment="Top"/>
<TabControl ItemsSource="{Binding ReportItems}" Grid.Row="1" Margin="0,20,0,0"
SelectedItem="{Binding SelectedReportItem}"
ContentTemplate="{StaticResource TabContent}"
ItemTemplate="{StaticResource TabHeader}"
/>
</Grid>
</ScrollViewer>
ScrollViewer presents to its child an infinitely large area on which to set itself out, because it reasons that it can just offer scrolling if it's bigger than the space available to ScrollViewer itself. Because yours has scrolling enabled in both directions, that means the TabControl can expand as much as it likes in either direction, and it's not going to be smart enough to know that it's inside a ScrollViewer and that you want the tabs to not take advantage of this virtual space.
From the sound of things, you might want to consider moving the ScrollViewer within the TabControl so that only the contents of the tab is scrollable rather than the whole set. You should be able to do that by modifying the tab content template.

WPF TabControl Children

This is my current Scenario: I have several UserControls inside different TabItems on a single TabControl in a WPF Window. Something Like:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="S.C.A.R" WindowState="Maximized">
<TabControl Name="MainTabControl">
<TabItem Name="TabOps">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Local:ServiceList Height="Auto" CanInsert="True" CanCollapse="True" Grid.ColumnSpan="3" x:Name="SL" RecordState="Edit"/>
<Local:ReservationList CanCollapse="True" Grid.Row="1" RecordState="Edit" x:Name="RL"/>
<Local:DriverList CanDelete="False" CanInsert="False" CanCollapse="True" Grid.Row="1" Grid.Column="2" RecordState="Edit" x:Name="DL"/>
<Local:CustomerForm CanDelete="False" CanInsert="False" Grid.Row="2" Grid.ColumnSpan="3" RecordState="View" x:Name="CL"/>
</Grid>
</TabItem>
<TabItemItem Name="TabCodes">
<Local:CustomerList x:Name="CustomerCRUD" RecordState="View"/>
</TabItem>
<Button Grid.Row="1" Content="TEST" Click="Button_Click"/>
</Grid>
</Border>
</Window>
Sorry for the indentation. For some reason I can't get the code properly indented here :(
What I need to do is to determine (preferably in the TabControl.Load Method, which of my different UserControls are currently visible. I need to do this in a dynamic way, I cannot hardcode the relationship between the TabItems and their children, something like:
if (TabControl.SelectedItem is XXXX)... is not possible here, because this is a Dynamic UI and I have no way to know which controls are there up front.
I've been digging a little bit and found out that the TabItem controls do not appear in the Visual tree of their "children". I only see a ContentPresenter, and then the TabControl itself. It looks like the tabItems do not "contain" their own content, so I could not, for example, do a FindAncestor to the Tab Items.
Another interesting fact is that the Loaded event of my usercontrols is being called on startup. Regardless of whether or not they're visible on screen.
An ideal scenario will be to find an event that is only fired on my Usercontrols when the TabItem they are under gets selected.
Appreciate any ideas. Thanks in advance
You should be able to leverage the VisualTreeHelper and consequentrly this answer on SO to provide the TabItem.Content returned object and look for the your specified type, UserControl in this instance.
NOTE:
For additional details please see the comments which transpired in the SO's question.

Resources