How to disable drag thumb on a ScrollBar in WPF? - wpf

Two question:
How to disable drag thumb on a ScrollBar in WPF?
Is there a way to limit drag thumb position? like LargeChange or SmallChange?

Edit the template of the ScrollBar control and set the IsEnabled property of the Thumb to false
Not sure you can do that directly from code or XAML but you might add 2 new DP to a control which inherits the ScrollBar class and then change the template to have this new feature

Maybe it will be usefull for someone
If you wanna disable drag thumb there two way:
Rewrite controll for your scrollbar
In controll template do not rewrite thumb, just use rectangle or just leave it empty
Example:
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background" Value="{StaticResource BackgroundColor}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="Bg" SnapsToDevicePixels="true">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
<RowDefinition Height="0.00001*"/>
<RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
</Grid.RowDefinitions>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Row="1" />
<RepeatButton Grid.Row="0" Command="ScrollBar.PageUpCommand" Style="{StaticResource ScrollBarButton}" Content="M 0 4 L 8 4 L 4 0 Z"/>
<Rectangle Grid.Row="1" VerticalAlignment="Top" x:Name="ThumbReplacer" Fill="{DynamicResource ScrollColor}"/>
<RepeatButton Grid.Row="2" Command="ScrollBar.PageDownCommand" Style="{StaticResource ScrollBarButton}" Content="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

Wpf how to create inner shadow like a sunken border

I am trying to accomplish the inner shadow in the following image.
It is a sort of sunken border looking shadow. I managed to come close with linear gradient fill but it needs some blur.
<Style TargetType="{x:Type local:LargeLabelWithUnitControl}">
<Setter Property="MaxHeight" Value="80"/>
<Setter Property="MinHeight" Value="80"/>
<Setter Property="MaxWidth" Value="130"/>
<Setter Property="MinWidth" Value="130"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:LargeLabelWithUnitControl}">
<Border BorderThickness="6" CornerRadius="15" >
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="10"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Display the LabelText -->
<TextBlock Text="{Binding LabelText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:LargeLabelWithUnitControl}}"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="Center"
FontSize="50"
FontFamily="Calibri"/>
<!-- Display the UnitText -->
<TextBlock Text="{Binding UnitText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:LargeLabelWithUnitControl}}"
Grid.Row="1"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="Center"
FontSize="20"
FontFamily="Calibri"/>
</Grid>
</Border>
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0, 0" EndPoint="0.3, 0.8" >
<GradientStop Color="#ff095750" Offset="0.0"/>
<GradientStop Color="#ff148F7F" Offset="1"/>
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Anyone knows how to do this?
So I have been on a quest to find a good answer to this very problem. So far there doesn't seem to be an easy or clean answer. This link here is good if your shape doesn't have rounded corners(or you want shading on all sides as seen in the rectangle example). https://www.codeproject.com/Articles/225076/Creating-Inner-Shadows-for-WPF-and-Silverlight
Though there doesn't seem to be a good solution for what you want. I have found something that works for my purposes, though it's not pretty in the slightest!
<Grid>
<Border Background="Yellow" Width="30" Height="30" Margin="-2" BorderThickness="2" CornerRadius="7" >
<!-- This blue border is hidden by the grey border -->
<Border Background="Transparent" BorderBrush="Blue" BorderThickness="1,1,0,0" CornerRadius ="5">
<Border.Effect>
<!-- This is where the actual inneshadow is defined -->
<DropShadowEffect ShadowDepth="0" BlurRadius="4" Color ="Red"/>
</Border.Effect>
</Border>
</Border>
<!-- Keeps blur radius from leaking out Normally it would be the color of the background -->
<Border BorderBrush="Green" Margin="-3" BorderThickness="3" CornerRadius="7" />
<!-- Grey border : If you want a borderless look I would recommend changing
this border to the background color as well or changing the size and
coverage of the green border -->
<Border BorderThickness="1" CornerRadius ="5" BorderBrush="Grey" />
</Grid>
Here is a link to the results https://imgur.com/zFh30jb, I added wacky colors so it should be clear which part is which. Again this is not a perfect solution and if anyone has anything better I would be interested. You will have to mess around with it a bit to get it to work for your purposes.
But I hope this helps someone out there!

WPF Flexible TabControl Header

I want to have a TabControl with multiple TabItems. These TabItems each have a header text. These texts may vary a lot in length (like 5 chars long and 15 chars long).
I want the TabControl to align the headers in one row only.
All tab headers should use the same width, and when there is enough space available, i want them the to use all the space available, up to a MaxWidth, that is the same for all items.
So if i want to use vMaxWidth` of 100 for 7 items, the tab header should be max 700 in width. If there is more space available, it should be ignored.
If there is less space available, i want that space to be distributed equally between the items. If the text gets cut off, i want to use TextWrapping.
I have tried multiple approaches to this problem now, this is my current setup:
<Style x:Key="Style-TabControl-Main" TargetType="{x:Type TabControl}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,0,1" Margin="13,0,0,0" BorderBrush="{StaticResource Brush-White}">
<StackPanel Panel.ZIndex="1" x:Name="HeaderPanel" IsItemsHost="True" KeyboardNavigation.TabIndex="1" Background="Transparent"
Orientation="Horizontal"/>
</Border>
<Border x:Name="Border"
Grid.Row="1" Grid.ColumnSpan="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the TabItem Style
<Style x:Key="Style-TabItem-Main" TargetType="{x:Type TabItem}">
<Setter Property="Height" Value="31"/>
<Setter Property="Width" Value="180" />
<Setter Property="Foreground" Value="{DynamicResource Brush-BrightRegular-Foreground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border x:Name="Border" Cursor="Hand"
Margin="2,0,0,0"
BorderThickness="1,1,1,0"
CornerRadius="4,4,0,0"
BorderBrush="{DynamicResource Brush-BrightRegular-Background}"
Background="{DynamicResource Brush-White}">
<ContentPresenter x:Name="Content" VerticalAlignment="Center" HorizontalAlignment="Stretch" ContentSource="Header" RecognizesAccessKey="True"
TextBlock.TextAlignment="Center" TextBlock.FontSize="16" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="{DynamicResource Brush-White}"/>
<Setter TargetName="Border" Property="Background" Value="{DynamicResource Brush-DefaultDark-Background}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am using a StackPanel instead of a TabPanel to get rid of the "stacking", that occurs, when you resize a default TabControl. However, i cannot get the rest of my requirements to work. I tried applying a MaxWidth (instead of fixed width) to the TabItem headers, but that of course doesn't work, because the item than shrinks to its minimum required size.
Step 1 (first attempt): Put headers in a single row, and give each header the same width.
This can be achieved by using a UniformGrid instead of the standard TabPanel, and lock its row count to 1. Here is a stripped-down version of your TabControl style:
<Style x:Key="Style-TabControl-Main" TargetType="{x:Type TabControl}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border>
<UniformGrid x:Name="HeaderPanel" IsItemsHost="True"
Rows="1" />
</Border>
<Border x:Name="Border" Grid.Row="1"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Step 2: Restrict headers to a MaxWidth and apply text wrapping.
The MaxWidth can be set in the TabItem style, along with a HeaderTemplate which wraps text (you can still use your custom ControlTemplate here to style the TabItem parts):
<Style x:Key="Style-TabItem-Main" TargetType="{x:Type TabItem}">
<Setter Property="MaxWidth" Value="100" />
<!--https://social.msdn.microsoft.com/forums/vstudio/en-US/df4f7fc3-f0ec-4ed1-a022-a32650e49cb3/how-to-wrap-header-text-in-tabcontrol-->
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" TextWrapping="Wrap" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
...
</Setter>
</Style>
Troubleshooting: Now, if you apply the MaxWidth in Step 2, you'll probably want to left-align the UniformGrid when the TabControl gets too wide..
<UniformGrid x:Name="HeaderPanel" IsItemsHost="True"
Rows="1" HorizontalAlignment="Left" />
..but you don't want that when the MaxWidth hasn't been reached yet, and the items should stretch across the entire width of the TabControl (aka Step 1). So we need a way to switch that HorizontalAlignment depending on whether the items' MaxWidth (if set) has been reached.
Step 1 (revisited): Let's try to make our own UniformGrid:
public class UniformTabPanel : UniformGrid
{
public UniformTabPanel()
{
this.IsItemsHost = true;
this.Rows = 1;
//Default, so not really needed..
this.HorizontalAlignment = HorizontalAlignment.Stretch;
}
protected override Size MeasureOverride(Size constraint)
{
var totalMaxWidth = this.Children.OfType<TabItem>().Sum(tab => tab.MaxWidth);
if (!double.IsInfinity(totalMaxWidth))
{
this.HorizontalAlignment = (constraint.Width > totalMaxWidth)
? HorizontalAlignment.Left
: HorizontalAlignment.Stretch;
}
return base.MeasureOverride(constraint);
}
}
Now, we can replace the UniformGrid in our TabControl style this new panel:
...
<Border>
<mycontrols:UniformTabPanel x:Name="HeaderPanel" />
</Border>
...
...and the TabControl should function as expeced.
Sphinxx answer is correct, however i needed to add the following code to the UniformTabPanel, to make it work like i want (resize the headers to maxwidth when enough space is available)
I added the following code to the UniformTabPanel, and it now does what i need:
protected override Size MeasureOverride(Size constraint)
{
var children = this.Children.OfType<TabItem>();
var totalMaxWidth = children.Sum(tab => tab.MaxWidth);
if (!double.IsInfinity(totalMaxWidth))
{
this.HorizontalAlignment = (constraint.Width > totalMaxWidth)
? HorizontalAlignment.Left
: HorizontalAlignment.Stretch;
foreach (var child in children)
{
child.Width = this.HorizontalAlignment == System.Windows.HorizontalAlignment.Left
? child.MaxWidth
: Double.NaN;
}
}
return base.MeasureOverride(constraint);
}

Round a arbitrary content control

I have an Esri ArcGis map control which I want to round around the edges. I am also using Prism4.0/MEF and SL4.
I tried to place it in a border, but that doesn't work (the Esri control is loaded into the MapRegion, in another module):
<Border Grid.Row="2"
Margin="2"
CornerRadius="25">
<ContentControl
prism:RegionManager.RegionName="MapRegion"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
</ContentControl>
</Border>
UPDATE: Looks like this is not possible. This isn't really a bug with the Map itself, but it kind of is. The Map uses a Canvas inside the Grid "RootElement". This Canvas holds the images for the map. When using a Canvas, it doesn't respect the bounds it's been given. You can reproduce the bug with the following XAML
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="25">
<Grid>
<Grid>
<Canvas>
<Image Source="/Images/MyPicture.png"/>
</Canvas>
</Grid>
</Grid>
</Border>
The best approach is going to be to set an explicit style for the map. With this style any map that is used will have the rounded corners
<Style TargetType="esri:Map">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="esri:Map">
<Border CornerRadius="25" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid>
<Grid x:Name="RootElement" Height="Auto" Width="Auto"/>
<Rectangle x:Name="ZoomBox" Fill="#55FFFFFF" Stroke="Red" StrokeThickness="2" Visibility="Collapsed"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

How do I restyle the XamNumericGrid up/down buttons?

In Infragistics NetAdvantage, how do I restyle the XamNumericEditor up/down buttons?
I looked in the DefaultStyles, and it's kind of tough to follow.
XamNumericEditor style is empty; it's simply an alias to XamMaskedEditor.
XamMaskedEditor, however, doesn't appear to have any up/down buttons defined in there. How does that work?
It'd be really great if I could find a solid not-overly-complex example.
Thanks to anyone who can help.
Here's the snippet from the DefaultStyle EditorsGeneric.xaml:
<Grid x:Name="PART_SpinButtons" DockPanel.Dock="Right" Visibility="{TemplateBinding SpinButtonVisibilityResolved}" Margin="0,1">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="1"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- AS 2/25/11 TFS67071 -->
<RepeatButton x:Name="spinUp" Style="{TemplateBinding SpinButtonStyle}" Focusable="false" ContentTemplate="{DynamicResource {x:Static igEditors:EditorsBrushKeys.IncreaseGlyphKey}}"/>
<RepeatButton x:Name="spinDown" Style="{TemplateBinding SpinButtonStyle}" Focusable="false" Grid.Row="2" ContentTemplate="{DynamicResource {x:Static igEditors:EditorsBrushKeys.DecreaseGlyphKey}}"/>
</Grid>
As you can see, you can set the SpinButtonStyle to change the look and feel of the spinButton in your application. If you want to change the content too (up/down arrows in the buttons), you can just override the EditorBrushKeys Resources.
Here's an example on how to change the style:
<Grid>
<Grid.Resources>
<!-- Change Content of the Repeat Buttons -->
<DataTemplate x:Key="{x:Static igEditors:EditorsBrushKeys.IncreaseGlyphKey}">
<Path
Width="7"
Height="4"
Data="M 0 4 L 8 4 L 4 0 Z"
Fill="{DynamicResource {x:Static igEditors:EditorsBrushKeys.DropdownBtnGlyphNormalForegroundFillKey}}"/>
</DataTemplate>
<DataTemplate x:Key="{x:Static igEditors:EditorsBrushKeys.DecreaseGlyphKey}">
<Path
Width="7"
Height="4"
Data="M 0 0 L 4 4 L 8 0 Z"
Fill="{DynamicResource {x:Static igEditors:EditorsBrushKeys.DropdownBtnGlyphNormalForegroundFillKey}}"/>
</DataTemplate>
<!-- Change properties on the button -->
<Style x:Key="mySpinButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Background" Value="Green"/>
</Style>
</Grid.Resources>
<igEditors:XamNumericEditor SpinButtonDisplayMode="Always" x:Name="myEditor" Value="10" Width="120" Height="40" SpinButtonStyle="{StaticResource mySpinButtonStyle}"/>
</Grid>

WPF multi-line TabControl without rearranging rows

The WPF TabControl with its default TabPanel arranges tab items into multiple rows when the horizontal size is too small. Then the tab selection changes the order of these rows, so the selected tab item is always in the first row.
I found several articles on how to replace TabPanel with another items control so instead of the multiline behavior they get scrolling tabs.
I would like to keep the multiple rows (no scrolling), but disable the rearrangement of rows. Once the tabs are created, they should stay in position, no matter how the selection changes. Is this possible?
have you tried overriding the default style with something like this? ie: using a wrappanel instead of a TabPanel?
<Style x:Key="{x:Type TabControl}" TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid TabNavigation="Local" SnapsToDevicePixels="true" ClipToBounds="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Name="ColumnDefinition0" />
<ColumnDefinition Name="ColumnDefinition1" Width="0" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Name="RowDefinition0" Height="Auto" />
<RowDefinition Name="RowDefinition1" Height="*" />
</Grid.RowDefinitions>
<WrapPanel Name="HeaderPanel" ZIndex="1" TabIndex="1" Column="0" Row="0" Margin="2,2,2,0" IsItemsHost="true" />
<Border Name="ContentPanel" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" TabNavigation="Local" DirectionalNavigation="Contained" TabIndex="2" Column="0" Row="1">
<ContentPresenter Name="PART_SelectedContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="{TemplateBinding Padding}" ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The only solution I found was modifying the framework's TabPanel class so that its int GetActiveRow(int[] solution) method always returns 0. Although this solves the problem, I'm not sure it is legal to use the framework's source in this way.
I think vigoo's answer is incomplete. int GetActiveRow(int[] solution) is a private method for the TabPanel class inside of the System.Windows.Controls.Primitives namespace. So you must create a copy of the TabPanel class from the WPF source code (which is available at WPF source github and place it in a new namespace.
// Returns the row which contain the child with IsSelected==true
private int GetActiveRow(int[] solution)
{
// Prevent Tabs from re-ordering when selecting a tab
return 0;
/*int activeRow = 0;
int childIndex = 0;
if (solution.Length > 0)
{
foreach (UIElement child in InternalChildren)
{
if (child.Visibility == Visibility.Collapsed)
continue;
bool isActiveTab = (bool)child.GetValue(Selector.IsSelectedProperty);
if (isActiveTab)
{
return activeRow;
}
if (activeRow < solution.Length && solution[activeRow] == childIndex)
{
activeRow++;
}
childIndex++;
}
}
// If the is no selected element and aligment is Top - then the active row is the last row
if (TabStripPlacement == Dock.Top)
{
activeRow = _numRows - 1;
}
return activeRow;*/
}
Then create a template for the TabControl and reference your new TabPanel in the Template (I called it overrides:TabPanel here). Don't forget to add a reference to the new TabPanel class in your ResourceDictionary (or wherever you defined the new style) xmlns:overrides="clr-namespace:MyNamespace.WPF.Overrides".
<Style x:Key="staticTabs"
TargetType="{x:Type TabControl}">
<Setter Property="Padding" Value="2" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Background" Value="{StaticResource TabItem.Selected.Background}" />
<Setter Property="BorderBrush" Value="{StaticResource TabItem.Selected.Border}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="templateRoot"
ClipToBounds="true"
KeyboardNavigation.TabNavigation="Local"
SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0" />
<ColumnDefinition x:Name="ColumnDefinition1"
Width="0" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0"
Height="Auto" />
<RowDefinition x:Name="RowDefinition1"
Height="*" />
</Grid.RowDefinitions>
<overrides:TabPanel x:Name="headerPanel"
Grid.Row="0"
Grid.Column="0"
Margin="2,2,2,0"
Panel.ZIndex="1"
Background="Transparent"
Grid.IsSharedSizeScope="True"
IsItemsHost="true"
KeyboardNavigation.TabIndex="1" />
<Border x:Name="contentPanel"
Grid.Row="1"
Grid.Column="0"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Finally your Tab control will reference this new Template:
<TabControl Style="{StaticResource staticTabs}" />

Resources