Expand/Collapse of a nested WPF Grid - c#

I have created a nested WPF Datagrid. I see you can expand it by clicking on the row, but it doesn't collapse when you do the same thing.
Few questions:
How can the user collapse the grid?
Is there a way to get Expand/Collapse buttons on the parent rows?
Whenever you click on a different parent to expand, it collapses the previous one you are on. Is there a way to get the grid to stay the way you make it. i.e. If I expand row 1 then go to row 3, row 1 will stay where I expanded it to.
Thanks,
Greg

There are couple approaches to reach such behavior.
For example
Expand/Collapse button in a Silverlight DataGrid.
If you prefer xaml then use:
<Style x:Key="ExpandableDataGridRowHeaderStyle"
TargetType="sdk:DataGridRowHeader">
<Setter Property="Background"
Value="#99E9EEF4" />
<Setter Property="IsTabStop"
Value="False" />
<Setter Property="SeparatorBrush"
Value="#FFFFFFFF" />
<Setter Property="SeparatorVisibility"
Value="Collapsed" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="sdk:DataGridRowHeader">
<Grid x:Name="Root">
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border BorderBrush="#FFFFFFFF"
BorderThickness="1,0,1,0"
Grid.ColumnSpan="2"
Grid.RowSpan="3">
<Grid>
<Rectangle x:Name="RowInvalidVisualElement"
Grid.ColumnSpan="2"
Fill="#FFF7D8DB"
Opacity="0"
Grid.RowSpan="3"
Stretch="Fill" />
<Rectangle x:Name="BackgroundRectangle"
Grid.ColumnSpan="2"
Fill="Transparent"
Grid.RowSpan="3"
Stretch="Fill" />
</Grid>
</Border>
<Button Background="{x:Null}"
BorderBrush="{x:Null}"
BorderThickness="0"
Padding="0"
Grid.RowSpan="1"
HorizontalAlignment="Stretch">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<AttachedBehaviors:ExpandButtonAction/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And AttachedBehavior that actually will change DetailsVisibility:
public class ExpandButtonAction : TargetedTriggerAction<FrameworkElement>
{
#region Invoke
protected override void Invoke(object parameter)
{
RoutedEventArgs eventArgs = (RoutedEventArgs) parameter;
Button bsender = eventArgs.OriginalSource as Button;
var row = DataGridRow.GetRowContainingElement(eventArgs.OriginalSource as FrameworkElement);
if (row.DetailsVisibility == Visibility.Visible)
{
row.DetailsVisibility = Visibility.Collapsed;
}
else
{
row.DetailsVisibility = Visibility.Visible;
}
}
#endregion
}
And apply this for RowHeaderStyle of DataGrid where you want to expand/collapse DetailsTemplate.

Use WPF Expander. So that you can expand the inner DataGrid.

Related

How do I change the button style for disabled WPF buttons?

I'm working on making a simple tic-tac-toe game to familiarize myself with WPF, due to the amount of time it's been since I've used the format. I've been trying to disable the button, to prevent people from choosing the same square, after the content has been set to X or O. Here is a sample of my XAML code for the buttons:
<Button Name="btmRight" Margin="10 10 10 10" Grid.Column="2" Grid.Row="2" FontSize="128">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="1"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="LightGray"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
The buttons are enabled by default. I then use some simple C# code to disable the button, and set the content to X or O depending on the turn. However, despite the style trigger being in each button, the format is never changed. Something I might explore is putting a conditional in the code, to not change anything with the buttons if they have content, rather than disabling the button when it's clicked. Here is a sample of what I'm using for the C#:
mid.Click += (sender, e) =>
{
mid.Content = mark;
TurnSwap();
mid.IsEnabled = false;
IsComplete();
};
"mark" is a String that is set depending on the turn. The content is set to the mark value, which then rotates immediately after the content is set. The button is then disabled, and the game checks to see if someone has won yet. If I were to go with the option of manually disabling the button, I would surround that C# code in a conditional, to check if the content = "", which it is by default. I feel this option is a bit lazy, and I would like to learn the WPF solution for the concepts alone.
The following code is just a sample based on your question. I does not have tic-tac-toe game logic implemented.
It demonstraits how to implement common style to button and also implmentation of common ClickEvent.
You can use it and change it as per you need.
XAML
<Window x:Class="WPFTest.MainWindow"
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"
xmlns:local="clr-namespace:WPFTest"
mc:Ignorable="d"
Title="TestWPF" Height="300" Width="400"
WindowStyle="None" WindowStartupLocation="CenterScreen">
<Window.Resources>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Background" Value="MediumAquamarine" />
<Setter Property="Foreground" Value="MediumBlue" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter x:Name="MyContentPresenter"
Content="{TemplateBinding Content}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="1"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="LightGray"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="btn1" Grid.Row="0" Grid.Column="0" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn2" Grid.Row="0" Grid.Column="1" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn3" Grid.Row="0" Grid.Column="2" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn4" Grid.Row="1" Grid.Column="0" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn5" Grid.Row="1" Grid.Column="1" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn6" Grid.Row="1" Grid.Column="2" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn7" Grid.Row="2" Grid.Column="0" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn8" Grid.Row="2" Grid.Column="1" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
<Button x:Name="btn9" Grid.Row="2" Grid.Column="2" Style="{StaticResource buttonStyle}" Click="btn_Click"/>
</Grid>
</Window>
CS
using System;
using System.Windows;
using System.Windows.Controls;
namespace WPFTest
{
public partial class MainWindow : Window
{
string sign = "X";
public MainWindow()
{
InitializeComponent();
}
private void btn_Click(object sender, RoutedEventArgs e)
{
try
{
Button currentButton = sender as Button;
currentButton.Content = sign;
currentButton.IsEnabled = false;
swapSign();
}
catch (Exception ex)
{
}
}
private void swapSign()
{
if (sign == "X")
sign = "O";
else
sign = "X";
}
}
}
UPDATE-1
I changed the style in XAML. Check above. One thing to note here is that you have to set Background and Foreground properties of Button Style like I set.

WPF ScrollViewer control buttons visibility

I have a ListBox, which has hidden Horizontal ScrollBar.
I have added custom buttons, to control this scroll (move left/move right).
I would like to hide (set visibility to collapsed) or something if scrollViewer does not contain enough items to work(When all child items fit in screen)
Is it possible in WPF?
EDIT:
Basically my view is kind of complicated, but I have something like this:
<ListBox x:Name="ListBox" Margin="0,0,10,0" Grid.Column="0" BorderThickness="0" ScrollViewer.HorizontalScrollBarVisibility="Hidden" Background="Transparent" ItemsSource="{Binding OpenedPatients}"
SelectedItem="{Binding SelectedPatient}">
...
...
</ListBox>
And I have controls with codebehind:
private void ButtonBase1_OnClick(object sender, RoutedEventArgs e)
{
_scrollViewer = FindVisualChild<ScrollViewer>(ListBox);
_scrollViewer.LineLeft();
_scrollViewer.LineLeft();
_scrollViewer.LineLeft();
_scrollViewer.LineLeft();
_scrollViewer.LineLeft();
}
private void ButtonBase2_OnClick(object sender, RoutedEventArgs e)
{
_scrollViewer = FindVisualChild<ScrollViewer>(ListBox);
_scrollViewer.LineRight();
_scrollViewer.LineRight();
_scrollViewer.LineRight();
_scrollViewer.LineRight();
_scrollViewer.LineRight();
}
The correct way to achieve what you want is to restyle the horizontal ScrollBar of the ListBox's ScrollViwer. You'd have to define a custom ControlTemplate for the ScrollViewer in which you simply substitute the original ScrollBar for an edited version of the ScrollBar with a custom ControlTemplate without the Track Thumb, but retaining the original RepeatButtons.
You can find the default ControlTemplate for the ScrollViewer in the ScrollViewer Styles and Templates page and the default ControlTemplate for the ScrollViewer in the ScrollBar Styles and Templates page on MSDN. If you need to, you can find out about ControlTemplates from the ControlTemplate Class page on MSDN.
As an example, adapted from the first linked page, you'd need to create a custom ControlTemplate for the horizontal ScrollBar as described above and apply it in a custom ControlTemplate for the ScrollViewer like this:
<Style x:Key="LeftScrollViewer"
TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="1"
BorderThickness="0,1,1,1">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<ScrollContentPresenter />
</Border>
<ScrollBar x:Name="PART_VerticalScrollBar"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<ScrollBar x:Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Template="{StaticResource YourCustomScrollBarTemplate}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

WPF C# DatePicker open calendar on click

I am adding a WPF DatePicker control to my form and it works fine. But I don't want the user to be able to type in a type in the 'Select a date' textbox. I want that to be readonly and when they click the textbox it just opens the calendar.
I wasn't sure if there was an option for this in the properties? I couldn't find anything...
<DatePicker HorizontalAlignment="Left" VerticalAlignment="Top">
<DatePicker.Resources>
<Style TargetType="DatePickerTextBox">
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="Text" Value=" "/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="IsEnabled" Value="False" />
<Setter Property="IsHitTestVisible" Value="False"/>
</Style>
</DatePicker.Resources>
</DatePicker>
In future you can use the WPF visualizer to see which child controls a top-level control is using (in this case DatePickerTextBox) and then apply a style and/or template to that type in the resources section like I've done here.
The answer of Mark Feldman is partially right, he had not answer to how clicking on the textbox gonna open the calendare popup!
You should hook the template of the DatePicker and enlarge the size of the button so it will overlap the textbox: then you are done, when you click on the textbox it's the button that get the event see below a part of the modified template of DatePicker:
<DatePickerTextBox Grid.Row="0" Grid.Column="0" x:Name="PART_TextBox" Focusable="{TemplateBinding Focusable}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
<Button Grid.Row="0" Grid.Column="0" x:Name="PART_Button" Foreground="{TemplateBinding Foreground}" Focusable="False" >
<Button.Template>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Background="Transparent"/>
<Button Grid.Column="1" HorizontalAlignment="Left" Foreground="{TemplateBinding Foreground}" Template="{StaticResource DropDownButtonTemplate}" VerticalAlignment="Top" Width="20" Margin="3,0,3,0" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
Dont forget to add the style of DatePickerTextBox described in the answer of Mark Feldman
You can open calendar this way :
<DatePicker Name="datePicker" Focusable="False">
<DatePicker.Resources>
<Style TargetType="DatePickerTextBox">
<EventSetter Event="MouseLeftButtonUp" Handler="OnMouseLeftButtonUp" />
</Style>
</DatePicker.Resources>
</DatePicker>
private void OnMouseLeftButtonUp(object sender, RoutedEventArgs e)
{
datePicker.IsDropDownOpen = true;
}

How to add Close Button in tab of Dynamic WPF Tab control

I have a WPF Tab Control..
i am adding tabs in that TabControl dynamically
Now i want to give a Close Button in every Tab of Tab Control..
So please tell me hopw to add Close button in that Tab Control..
Code for adding tab
private void AddTab(ITabbedMDI mdiChild)
{
if (_mdiChildren.ContainsKey(mdiChild.UniqueTabName))
{
//user control is already opened in tab.
//So set focus to the tab item where the control hosted
foreach (object item in tcMdi.Items)
{
TabItem ti = (TabItem)item;
if (ti.Name == mdiChild.UniqueTabName)
{
ucChildLoc = (UserControl)mdiChild;
ti.Focus();
//tcMdi.Width = this.ucChildLoc.Width;
//tcMdi.Height = this.ucChildLoc.Height;
break;
}
}
}
}
Code for Closing tab
private void CloseTab(ITabbedMDI tab, EventArgs e)
{
TabItem ti = null;
foreach(TabItem item in tcMdi.Items)
{
if (tab.UniqueTabName == ((ITabbedMDI)item.Content).UniqueTabName)
{
ti = item;
break;
}
}
if (ti != null)
{
_mdiChildren.Remove(((ITabbedMDI)ti.Content).UniqueTabName);
tcMdi.Items.Remove(ti);
}
}
I am using TabControl of this article
http://www.codeproject.com/Articles/32362/Tabbed-MDI-in-WPF
Thanks in Advance..
I have tried several solutions and struggled to find something that looked nice and also highlighted the "X" in the button when the mouse hovers. I finally ended with this one. It also does not require too much code. I hope it helps:
<TabControl>
<TabItem>
<TabItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0">Output</TextBlock>
<Button Grid.Column="1" Name="button_close" Click="button_close_Click">
<Button.Template>
<ControlTemplate TargetType="Button">
<Path Data="M0,0 L8,8 M8,0 L0,8" StrokeThickness="3" VerticalAlignment="Center" Margin="5,4,0,2">
<Path.Style>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Stroke" Value="LightGray" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</TabItem.Header>
<TabItem.Content>
</TabItem.Content>
if you make the tab control as a custom control and inherit the functionality from tab control and then add close button and handle its events then it can work
<UserControl x:Class="CloseableHeader"
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="23" d:DesignWidth="81" Margin="0">
<Grid>
<Button Content="X" Height="19" HorizontalAlignment="Right" Margin="0,3,4,0"
Name="button_close" VerticalAlignment="Top" Width="20" FontFamily="Courier"
FontWeight="Bold" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
FontStretch="Normal" Visibility="Visible"
FontSize="14" Padding="0" ToolTip="Close"/>
<Label Content="TabItem" Height="23" HorizontalAlignment="Left"
Margin="4,1,0,0" Name="label_TabTitle" VerticalAlignment="Top"
FontFamily="Courier" FontSize="12" />
</Grid>
class ClosableTab : TabItem
{
// Constructor
public ClosableTab()
{
// Create an instance of the usercontrol
closableTabHeader = new CloseableHeader();
// Assign the usercontrol to the tab header
this.Header = closableTabHeader;
}
}
see in this article for details http://www.codeproject.com/Articles/84213/How-to-add-a-Close-button-to-a-WPF-TabItem
this might be helpful
TabControl does not provide the ability to close TabItems.
You could add a 'x' button and set the visibility to Collapsed / Hidden as a hack.
OR You could look at XamTabControl by Infragistics or any other vendor product which does support closing of Tabs.

How I can hide a Button inside a Control Template?

How can I set Visibility="Visible" for the Button inside the Control Template when the IsSendBtnVisible property in the code-behind is true?
Here's my WPF page:
<Page
x:Class="CardViewPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CardViewPage">
<Grid Name="content" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DocumentViewer Margin="0" Grid.Row="0" Name="documentViewer" />
</Grid>
</Page>
Here's my Custom Template for the document viewer on this page:
<Style TargetType="{x:Type DocumentViewer}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DocumentViewer}">
...
<Button
Click="btnSendToServer_Click"
Width="25"
Height="25"
Visibility="Collapsed" />
...
</ControlTemplate>
</Setter>
...
</Style>
U need to declare DependancyProperty for ur DocumentViewer and use TemplateBinding in xaml ControlTemplate (UrProperty for example)
<ControlTemplate TargetType="{x:Type DocumentViewer}">
...
<Button Click="btnSendToServer_Click"
Width="25"
Height="25"
Visibility="{TemplateBinding UrProperty}"
/>
...
</ControlTemplate>
I suggest you to use data triggers to achieve this...
<Button
Click="btnSendToServer_Click"
Width="25"
Height="25">
<Button.Style>
<Style>
<Setter Property="Button.Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSendBtnVisible}" Value="True">
<Setter Property="Button.Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Make sure to set visibility to collapsed in the style but not in the button properties..
Note: Binding for data trigger may change depending on your data context
Dima Martovoi,bathineni - thanks for replies.
Dima Martovoi, i think inherit from DocumentViewer is to hard for this small problem.
I tried to use variant with DataTrigger from bathineni's solution, but it's not works. Don't know, why.
Problem was solved using next binding:
<Button
Visibility="{Binding RelativeSource={RelativeSource AncestorType=Page},Path=SendToServerVisiblity}">
</Button>
where
public Visibility SendToServerVisiblity
{
get
{
if (IsOnlineMode)
return Visibility.Visible;
return Visibility.Collapsed;
}
}
in page code-behind

Categories

Resources