Put tab headers in their own grid row? - c#

I have a TabControl inside a Grid. Is there a way to assign the tab "headers area" to fill a particular grid row, and the tab content area to fill another?
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TabControl x:Name="tabControl">
<TabItem Header="TabItem">
</TabItem>
<TabItem Header="TabItem">
</TabItem>
</TabControl>
</Grid>

So like #xtreampb pointed out, you will need to make some modifications if you truly want to separate the functionality out.
HOWEVER, you do have multiple options to accomplish this if you don't want to dig through the control templates. For example, the last time I had to facilitate such a request I only had about 15 minutes to do it. So what I ended up doing was like the concept shown below and just made a RadioButton style to match the design of the "Tabs" they wanted.
Was quick and painless (under 5 minutes) and served the purpose at the time. Plus the app was really small and the owner's didn't care about much else than the design they wanted.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="20">
<RadioButton GroupName="Blah" Content="Blah1"
IsChecked="{Binding IsSelected, ElementName=Blah1}"/>
<RadioButton GroupName="Blah" Content="Blah2" Margin="10,0"
IsChecked="{Binding IsSelected, ElementName=Blah2}"/>
<RadioButton GroupName="Blah" Content="Blah3"
IsChecked="{Binding IsSelected, ElementName=Blah3}"/>
</StackPanel>
<TabControl Grid.Row="1">
<TabItem x:Name="Blah1" Visibility="Collapsed">
<TextBlock Text="Wow TAB 1 IsSelected!"/>
</TabItem>
<TabItem x:Name="Blah2" Visibility="Collapsed">
<TextBlock Text="Wow TAB 2 IsSelected!"/>
</TabItem>
<TabItem x:Name="Blah3" Visibility="Collapsed">
<TextBlock Text="Wow TAB 3 IsSelected!"/>
</TabItem>
</TabControl>
</Grid>
Hope this helps, cheers!

Related

Why is my DataGrid going off screen no matter what solution I try?

I am new to C# and WPF trying to get what I should think is a simple thing, but it doesn't work.
I have a data grid being populated by SQL, and no matter what I try, I can't get the Height of the DataGrid to stay within the window. It always just extends down. I want it to be dynamic to the window size.
My very simple code is below, or at least this most recent iteration.
<Page x:Class="TMSMaintenance.PaymentError"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TMSMaintenance"
Title="PaymentError">
<!--<DataGrid Name="MydataGrid" CanUserAddRows="False" SelectionMode="Single" />-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" VerticalAlignment="Stretch" >
<DataGrid x:Name="MydataGrid"
VerticalAlignment="Stretch"
MinHeight="100"
VerticalScrollBarVisibility="Auto" VerticalContentAlignment="Stretch">
</DataGrid>
</DockPanel>
</Grid>
</Page>
I have tried wrapping in a ScrollView - it didn't work. I tried setting the Height by binding it to the window - it didn't work. I have tried the Grid.RowDefinition Height = "*" and "1*" - it didn't work. VerticalAlignment = "Stretch" also didn't work.
So what am I missing?
Edit: Maybe I should also say that this is on a Page file called within a frame tag. Not sure if it makes a difference here.
<StackPanel CanVerticallyScroll="True" CanHorizontallyScroll="True">
<!-- Navigation -->
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Button Content="Files Lookup" Margin="0,0,10,0"/>
<Button Content="Payment Error" Margin="0,0,10,0"/>
<Button Content="Carrier Maintenance" Margin="0,0,10,0"/>
<Button Content="Payment File" Margin="0,0,10,0" />
</StackPanel>
<ScrollViewer>
<Frame x:Name="MainFrame" NavigationUIVisibility="Hidden" ></Frame>
</ScrollViewer>
</StackPanel>
Get rid of that DockPanel. Grid is a much more flexible container for controls and you don't need to pack container into container at all. This alone should do the trick.
Good practice is to not use DockPanels at all. Never. Everything you can achieve with DockPanels can be achieved with Grids (with a bit more of coding, but it gives you more flexible solution and better maintainability of your code).
Also get rid of VerticalContentAlignment (not needed in case you described) and you don't need to define VerticalAlignment (nor HorizontalAlignment) to Stretch, since it's a default value of that property.
EDIT:
I haven't noticed the second sample of your code. Everything I wrote before still applies and will make your code better, but I think your problem is with nesting your Page in your main container (Window or whatever it is).
Try replacing:
<StackPanel CanVerticallyScroll="True" CanHorizontallyScroll="True">
<!-- Navigation -->
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Button Content="Files Lookup" Margin="0,0,10,0"/>
<Button Content="Payment Error" Margin="0,0,10,0"/>
<Button Content="Carrier Maintenance" Margin="0,0,10,0"/>
<Button Content="Payment File" Margin="0,0,10,0" />
</StackPanel>
<ScrollViewer>
<Frame x:Name="MainFrame" NavigationUIVisibility="Hidden" ></Frame>
</ScrollViewer>
</StackPanel>
To:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal"
Margin="0,10,0,0">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="0,0,10,0" />
</Style>
</StackPanel.Resources>
<Button Content="Files Lookup"/>
<Button Content="Payment Error"/>
<Button Content="Carrier Maintenance"/>
<Button Content="Payment File"/>
</StackPanel>
<Frame x:Name="MainFrame"
Grid.Row="1"
NavigationUIVisibility="Hidden" />
</Grid>
I have also simplified your styling on Buttons and I'd recommend you to change your Page to UserControl.

Binding Button to its handler by convention doesn't work

I have a WPF application with caliburn.micro. There is a user control MyView in a tab item of a tab control. Within that user control, there is another tab control. In one of its tabs, I added a button, and a corresponding method with the same name in the MyViewModel. But this method is not called when I click the button. Could you please tell what might cause it?
Thanks.
In the View:
<TabControl SelectedIndex="{Binding SelectedTabIndex}">
...
<TabItem x:Name="TextTab" Header="Text">
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
...
</ScrollViewer>
<Button Grid.Row="1" x:Name="SaveText" Content="Save" Width="50" Height="25" />
</Grid>
</TabItem>
In the ViewModel:
public void SaveText()
{
...
}
I found a solution:
<Button Grid.Row="1" x:Name="SaveText" cal:Message.Attach="SaveText" Content="Save" Width="50" Height="25" />
Still don't know why the convention didn't work without "Attach".

Updating data binding source of a content control in a ResourceDictionary

I want to create a view that has a set of tabs (each, basically a ContentControl) that each have various settings. I then want to have a button that will update all of the data binding rather than updating instantly or having update buttons associated with the controls themselves
So, my control is MEF exported as a ResourceDictionary and is similar to the below
<ResourceDictionary ...>
<DataTemplate DataType="{x:Type vm:AdminViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TabControl Grid.Row="0">
<TabItem Header="Tests">
<ContentControl Content="{Binding ResultStorage}"/>
</TabItem>
</TabControl>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Content="Update"/>
<Button Content="Cancel"/>
</StackPanel>
</Grid>
</DataTemplate>
</ResourceDictionary>
TestStorage would be like this:
<ResourceDictionary ...>
<DataTemplate DataType="{x:Type data:XmlResultStorage}">
<StackPanel>
<TextBlock Text="Result File Path:"/>
<TextBox Text="{Binding Path=ResultPath, Source={x:Static properties:DataStorage.Default}, UpdateSourceTrigger=Explicit}"/>
<TextBlock Text="Result File Location:"/>
<TextBox Text="{Binding Path=ResultFilename, Source={x:Static properties:DataStorage.Default}, UpdateSourceTrigger=Explicit}"/>
</StackPanel>
</DataTemplate>
What I want to do is when the button to update is pressed to somehow call the update (UpdateSource?) on the ContentControl but I can't see how to do it.
In an ideal world I wouldn't have code-behind and do it all via MVVM or something, but if that's not possible code-behind is fine.
So I have two issues, how do I update data bindings manually via a ResourceDictionary and how do I then cause it to cascade through its child ContentControls?

Creating a button Template

I'm trying to create a template for a button that I can use over and over again on a form.
The Button, I want it to contain a Grid with two rows and a custom piece of text within the bottom row.
This is what I've got so far, but I don't think it's right because I want to set the text from within the button element.
<ControlTemplate TargetType="Control">
<Grid Width="444">
<Grid.RowDefinitions>
<RowDefinition Height="51" />
<RowDefinition Height="36" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#286c97"></Grid>
<Grid Grid.Row="1" Background="#5898c0">
<TextBlock Grid.Row="1" FontFamily="Segoe UI" FontSize="12" Text="{TemplateBinding Content}" />
</Grid>
</Grid>
</ControlTemplate>
Then to call the template I was hoping I could go:
<Button Content="This is the text" />
But sadly this doesn't work. Is there some other template that I'm supposed to be using to pass the text value to it?
To make it work, there is a control called ContentPresenter. Place that inside your template wherever you want it to be. But remember, that it could be anything, a text, an image or a bunch of other controls, and your Button nor your ControlTemplate, should not care about what it is.
ControlTemplate TargetType="Control">
<Grid Width="444">
<Grid.RowDefinitions>
<RowDefinition Height="51" />
<RowDefinition Height="36" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#286c97"></Grid>
<Grid Grid.Row="1" Background="#5898c0">
<ContentPresenter/>
</Grid>
</Grid>
</ControlTemplate>
The ContentPresenter, when used inside a ContentControl, like the button, automatically attaches to the Content, ContentTemplate and ContentTemplateSelector properties of the templated parent.
Now if you want to display more than just Text, or want to customize the text more, just pass a DataTemplate as your ContentTemplate directly to the specific button.
<DataTemplate x:Key="myButtonContentTemplate">
<TextBlock FontSize="18" Text="{Binding}"/>
</DataTemplate>
<Button ContentTemplate="{StaticResource myButtonContentTemplate}"/>

WPF Issues with Control Layout

I am making an application that connects to our billing software using its API, and I am running into a few issues getting the layout working properly.
I want to make it so that when one of the expanders is minimized, the other window fills the gap, and when it is expanded again the other expander goes back to where it was. Right now when the arrow is clicked on one, there is just an empty gap. I used a DockPanel as the parent which I assumed would automatically do this, but it isn't working.
Second question, is there a way to make these areas resizable? I don't want to try and get too frisky with allowing the user to undock the menus (don't even know if that is possible with just straight WPF) but it would be nice if they could change the width/height of them.
Also, just a newbie question to C#, but what is the equivalent of a C++ header file? It looks like you just use .cs files, but I am not sure. I want to extract all of my functions that pull the data from the billing software and put them into a different file to clean up the code.
Here is my XAML...
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Billing Management" Height="550" Width="754" xmlns:shared="http://schemas.actiprosoftware.com/winfx/xaml/shared" WindowStartupLocation="CenterScreen" WindowStyle="ThreeDBorderWindow">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu Height="22" Name="menu1" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Top" HorizontalContentAlignment="Left" IsEnabled="True" IsMainMenu="True">
<MenuItem Header="_File">
<MenuItem Header="_Open" />
<MenuItem Header="_Close" />
<Separator/>
<MenuItem Header="_Exit" />
</MenuItem>
</Menu>
<TabControl Name="tabControl1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" BorderThickness="1" Padding="0" TabStripPlacement="Bottom" UseLayoutRounding="False" FlowDirection="LeftToRight" Grid.Row="1">
<TabItem Header="Main" Name="tabItem1" Margin="0">
<DockPanel Name="dockPanel1" LastChildFill="True">
<ListBox Height="100" Name="listBox3" DockPanel.Dock="Top" />
<ListBox Name="listBox4" Width="200" DockPanel.Dock="Right" />
<DockPanel Height="Auto" Name="dockPanel2" Width="Auto" VerticalAlignment="Stretch" LastChildFill="True">
<shared:AnimatedExpander Header="Staff Online" Width="200" Name="expanderStaffOnline" IsExpanded="True" Height="194" BorderThickness="0" DockPanel.Dock="Top" VerticalContentAlignment="Stretch">
<ListBox Name="listboxStaffOnline" Width="Auto" Height="Auto" Margin="0" VerticalAlignment="Stretch" Loaded="listboxStaffOnline_Loaded" />
</shared:AnimatedExpander>
<shared:AnimatedExpander Header="Test Menu 2" Height="Auto" Name="animatedExpander1" BorderThickness="1" Margin="0,0,0,0" IsExpanded="True" VerticalContentAlignment="Stretch">
<ListBox Height="Auto" HorizontalAlignment="Stretch" Name="listBox6" VerticalAlignment="Stretch" Margin="0" BorderThickness="1" />
</shared:AnimatedExpander>
</DockPanel>
<ListBox Height="100" Name="listboxAdminLogs" DockPanel.Dock="Bottom" Loaded="listboxAdminLogs_Loaded" />
<ListBox Name="listBox5" />
</DockPanel>
</TabItem>
<TabItem Header="Support" Name="tabItem2" Margin="0">
</TabItem>
<TabItem Header="Clients" />
<TabItem Header="Billing" />
<TabItem Header="Orders" />
</TabControl>
</Grid>
</Window>
You should place your expanders in a Grid which will allow the content to expand and fill automatically. providing the Row / Column definitions width / height are set to Auto or *.
If you want to resize you will need to use the GridSplitter; see http://www.wpftutorial.net/GridLayout.html for further details.
I don't know C++ so can't really tell you want the equivalent of header files are but I think what you are trying to do is achievable using partial classes; see http://msdn.microsoft.com/en-us/library/wa80x488(v=vs.80).aspx for further details.
And finally, to answer your question in your comments; To get a uniform appearance for all controls in WPF you would need to create custom Styles which use a common set of Colors / Brushes.
I suggest you put the expanders in a Grid and set the row height of the gridrows to auto.
To make it resizable simply add GridSplitters in inbetween rows of this grid.
As for your added question: I think it would be better to copy the style of the expanders' headers and use it for a label or other header.
Also (many questions in this one): C# has no header files.

Categories

Resources