Flip a UIElement but preserve text inside from flipping - wpf

I think the title is pretty straightforward. I'm using some custom controls. I want to flip the tab header of a custom tab control. I tried a layout transform (ScaleTransform X = -1) to flip horizontally the tab header. But obviously I want the text inside not to be mirrored. I can't find a way so far.

You can do this by giving the TabItem a HeaderTemplate, and applying a ScaleTransform there also:
<TabControl>
<TabItem Header="Hello, World!">
<TabItem.LayoutTransform>
<ScaleTransform ScaleX="-1" />
</TabItem.LayoutTransform>
<TabItem.HeaderTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}">
<ContentPresenter.LayoutTransform>
<ScaleTransform ScaleX="-1" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
</TabControl>

Related

MahApps.Metro AnimatedSingleRowTabControl doesn't work

For some reason my Tab Control doesn't seem to work as described.
Instead of seeing this (From Mahapps site)
I see this
I have tried fiddling with every single property to try to get the scroll bar to go away and to have left/right scroll buttons as shown in the example, but absolutely nothing works.
What am I missing?
EDIT: - Added my XAML
<Controls:MetroAnimatedSingleRowTabControl Controls:TabControlHelper.IsUnderlined="True" Margin="5" ScrollViewer.PanningMode="Both" ScrollViewer.CanContentScroll="False" ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<Controls:MetroTabItem Header="Thread Image Download">
</Controls:MetroTabItem>
<Controls:MetroTabItem Header="Random Board Stats">
</Controls:MetroTabItem>
</Controls:MetroAnimatedSingleRowTabControl>
It doesn't exist a style key for this TabControl. So you must add the resource dictionary to the place where you need it. So MahApps should solve this in the next releases (site note for me).
<Grid>
<Grid.Resources>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedSingleRowTabControl.xaml" />
</Grid.Resources>
<TabControl>
<TabItem Header="this tabcontrols tabs">
<TextBlock FontSize="30" Text="Content" />
</TabItem>
<TabItem Header="appear only on a single line">
<TextBlock FontSize="30" Text="Content" />
</TabItem>
<TabItem Header="if they are overflowing">
<TextBlock FontSize="30" Text="Content" />
</TabItem>
<TabItem Header="instead of wrapping them">
<TextBlock FontSize="30" Text="Content" />
</TabItem>
</TabControl>
</Grid>
Hope this helps.

WPF : dynamic view/content

I'm a bit beginner in WPF, so I ask this..
Let's say I have a window, and inside the window I want to have something like container, could be just border or maybe panel (in winform terms). The content of container is binded to the selected option (e.g:button). So, for instance, when user selects OPTION 1, the container shows chart; when user selects OPTION 2, the container shows listview filled with data; when user selects OPTION 3, the container shows another things, and so on.
What is the best/nicest (or easiest maybe) approach to do this? I'm thinking about using user control for the content of the container, but don't know if this is nice solution neither the performance for using user control to show little bit complex things and maybe some calculations. Any other idea guys?
To elaborate on #Sheridan's answer, here is a simple TabControl XAML that does what you need:
<TabControl TabStripPlacement="Left">
<TabItem Header="Option 1">
<Grid Background="DarkGray">
<TextBlock Foreground="AliceBlue" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" Text="View 1"/>
</Grid>
</TabItem>
<TabItem Header="Option 2">
<Grid Background="DarkBlue">
<TextBlock Foreground="AliceBlue" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" Text="View 2"/>
</Grid>
</TabItem>
<TabItem Header="Option 3">
<Grid Background="DarkSlateBlue">
<TextBlock Foreground="AliceBlue" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" Text="View 3"/>
</Grid>
</TabItem>
</TabControl>
Result:
You can customize it a little bit by adding this simple Style To your Window.Resources:
<Window.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<RadioButton Content="{TemplateBinding Header}" Margin="2"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Which then results in:
The "WPF Mentality" makes you think the UI controls in terms of their functionality, not their appearance, this is a TabControl =)
I solved this with a ContentControl
MainWindow:
(Define the views you wish to visualize as resources)
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:SystemPCViewModel}">
<controls:SystemPCControl/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:ChipPCViewModel}">
<controls:ChipPCControl/>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentView}"/>
</Grid>
ViewModel: (can't get much simpler)
public ViewModelBase CurrentView
{
get { return currentView; }
set { Set(() => CurrentView, ref currentView, value); }
}
And there you go, you can change your views by setting the view model for the controls you defined in your MainWindow
private void OnCommandExecuted()
{
CurrentView = someViewModel;
}
private void OnAnotherCommandExecuted()
{
CurrentView = anotherViewModel;
}
HTH!
What you are describing sounds pretty close to a standard TabControl, but with a ControlTemplate that puts the tabs on the left side instead of above the content panel. Using this method would mean having a UserControl in each TabItem, eg. multiple controls. You can find out more about the TabControl from the TabControl Class page at MSDN.

Weird transform behavior with Images in WPF ListView

I've got a DataGrid with multiple columns. In one of the inner columns, I am displaying an image. This image needs to be rotated, so I used a TransformGroup to scale and rotate the image as needed.
If I do no rotate, using the following XAML, the image appears normally. As I change the width of the image column, the image scales as expected. It stays within the bounds of its column:
<GridViewColumn Header="Image" Width="200" x:Name="image_column">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=ImagePath}" Margin="5">
</Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Now if I attempt to rotate the image as necessary within the image column, things get weird. The column is still tied to the non-rotated image width, and the row height is tied to the non-rotated image height. The image also intrudes on its left neighbor's space. This is one variant of XAML that I have tried:
<GridViewColumn Header="Image" Width="200" x:Name="image_column">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=ImagePath}" Margin="5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="0.8" ScaleY="0.8" />
<RotateTransform Angle="90" CenterX="0" CenterY="0" />
<TranslateTransform X="{Binding ElementName=image_column, Path=Width}" />
</TransformGroup>
</Image.RenderTransform>
</Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
The TranslateTransform above is a complete hack to at least make the right edge of the image line up with the right edge of its column.
I've tried to add a Viewbox, thinking that the it would scale the way I want it to, with the rotated the image inside of it, but that didn't work.
Can anyone tell me what I am fundamentally missing here, and offer hints on how I can approach a solution? The only idea I have at this point is to rotate the image and save to disk before display.
You should set the LayoutTransform property instead of RenderTransform.
<Image.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleX="0.8" ScaleY="0.8"/>
<RotateTransform Angle="90"/>
</TransformGroup>
</Image.LayoutTransform>
As the column width is fixed, the ScaleTransform seems to be redundant.
<Image.LayoutTransform>
<RotateTransform Angle="90"/>
</Image.LayoutTransform>

WPF: How to set background of TabItem?

How to set the background of TabItem? I tried the following code:
<TabControl>
<TabItem Header="Test" Background="Blue" Foreground="Red" />
</TabControl>
Foreground works, but Background does not work.
Any ideas? Thanks
What is happening is that in the case of a single tab, it is always selected, and so you are only seeing the selection style of the tab item.
For example, take a look at the following TabControl:
<TabControl>
<TabItem Header="Tab A" Background="Blue" Foreground="Red">
<Grid />
</TabItem>
<TabItem Header="Tab B" Background="Green" Foreground="Navy" >
<Grid />
</TabItem>
<TabItem Header="Tab C" Background="LightBlue">
<Grid />
</TabItem>
</TabControl>
Tab A will not display its Blue background until you select a different tab. If you truly want the Background to remain the same regardless of whether it is selected or not, you will need to override the control template of the TabItem.
See the question TabItem Background color changes when tabitem selected or hover over for an example of how to do this.

WPF: Resizing a circle, keeping the center point instead of TopLeft?

I'd like to resize a circle on my canvas with the help of a slider. This circle can be moved around on the canvas by some drag&drop stuff I did in code behind, so its position is not fixed.
I have bound the slider's value to an ellipse's height and width. Unfortunately, when I use the slider, the circle gets resized with its top left point (actually the top left point of the rectangle it's sitting in) staying the same during the operation.
I would like to resize it with its center point being constant during the operation. Is there an easy way to do this in XAML? BTW, I already tried ScaleTransform, but it didn't quite do what I wanted.
Thanks a bunch! :-)
Jan
<Canvas x:Name="MyCanvas">
<!-- this is needed for some adorner stuff I do in code behind -->
<AdornerDecorator Canvas.Left="10"
Canvas.Top="10">
<Ellipse x:Name="myEllipse"
Height="{Binding Path=Value, ElementName=mySlider}"
Width="{Binding Path=Value, ElementName=mySlider}"
Stroke="Aquamarine"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5">
<Ellipse.RenderTransform>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}" />
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
<Slider x:Name="mySlider"
Maximum="100"
Minimum="0"
Width="100"
Value="10"
Canvas.Left="150"
Canvas.Top="10" />
<Slider x:Name="myRotationSlider"
Maximum="360"
Minimum="0"
Width="100"
Value="0"
Canvas.Left="150"
Canvas.Top="50" />
</Canvas>
You can bind your Canvas.Left and Canvas.Top to your Height and Width via a ValueConverter.
Specifically (edit):
Create a property each for the Canvas.Left and Canvas.Top and bind to these.
Store the old values for Width and Heigth or the old slider value.
Whenever the slider is changed, get the incremental change "dx" by subtracting the stored value.
(Don't forget to update the stored value...)
Add dx to Width and Height property.
And, as Will said, add dx/2*-1 to Canvas.Left and Canvas.Top properties.
Does that make sense?
The problem is that you are using the SLIDER to adjust the width and height. Width and height are not calculated around RenderTransformOrigin; only RenderTransforms use that value.
Here's a corrected version (brb, kaxaml):
<Canvas x:Name="MyCanvas">
<!-- this is needed for some adorner stuff I do in code behind -->
<AdornerDecorator Canvas.Left="50" Canvas.Top="50">
<Ellipse
x:Name="myEllipse"
Width="10"
Height="10"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5"
Stroke="Aquamarine">
<Ellipse.RenderTransform>
<TransformGroup>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}"/>
<ScaleTransform
CenterX=".5"
CenterY=".5"
ScaleX="{Binding Path=Value, ElementName=mySlider}"
ScaleY="{Binding Path=Value, ElementName=mySlider}"/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
<Slider
x:Name="mySlider"
Width="100"
Canvas.Left="150"
Canvas.Top="10"
Maximum="10"
Minimum="0"
SmallChange=".01"
Value="1"/>
<Slider
x:Name="myRotationSlider"
Width="100"
Canvas.Left="150"
Canvas.Top="50"
Maximum="360"
Minimum="0"
Value="0"/>
</Canvas>
Of course, this will probably not work for you. Why? Well, the ScaleTransform I used zooms not only the circle but also the border; as the circle gets bigger the border does as well. Hopefully you won't care about this.
Also, realize when combining transforms (scale then rotate in this case) that they are applied in order, and one may affect how another is done. In your case, you would not notice this. But if, say, you were doing a rotate and translate, the order would be relevant.
Ah, what was I thinking? Just stick the ellipse in a Grid (simplest solution but other containers would work). The grid automatically takes care of centering the ellipse as it is resized. No need for any value converters! Here's the code:
<Canvas x:Name="MyCanvas">
<!-- this is needed for some adorner stuff I do in code behind -->
<Grid Width="100" Height="100">
<AdornerDecorator>
<Ellipse
x:Name="myEllipse"
Width="{Binding Path=Value, ElementName=mySlider}"
Height="{Binding Path=Value, ElementName=mySlider}"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5"
Stroke="Aquamarine">
<Ellipse.RenderTransform>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}"/>
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
</Grid>
<Slider
x:Name="mySlider"
Width="100"
Canvas.Left="150"
Canvas.Top="10"
Maximum="100"
Minimum="0"
Value="10"/>
<Slider
x:Name="myRotationSlider"
Width="100"
Canvas.Left="150"
Canvas.Top="50"
Maximum="360"
Minimum="0"
Value="0"/>
</Canvas>
Since you're using a Canvas, the location an element has is the location. If you want the Top,Left position to change you need to do it yourself. If you were using another Panel type, like a Grid, you could change the alignment of your Ellipse to place it in the same relative location no matter what the size. You could get that effect by adding a Grid inside your AdornerDecorator and centering the Ellipse but you'd also need to set the AdornerDecorator or Grid to a fixed size because they won't stretch in a Canvas.
The best solution you could use would be a ScaleTransform applied to the RenderTransform property with a RenderTransformOrigin of 0.5,0.5. You said you had problems with ScaleTransform but not what the problem was.
Wrap your Ellipse in a Grid of the maximum size. As long as it is smaller, the Ellipse will be centered in the Grid:
<Grid
Canvas.Left="10"
Canvas.Top="10"
Width="100"
Height="100">
<AdornerDecorator>
<Ellipse x:Name="myEllipse"
Height="{Binding Path=Value, ElementName=mySlider}"
Width="{Binding Path=Value, ElementName=mySlider}"
Stroke="Aquamarine"
Fill="AliceBlue"
RenderTransformOrigin="0.5 0.5">
<Ellipse.RenderTransform>
<RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}" />
</Ellipse.RenderTransform>
</Ellipse>
</AdornerDecorator>
</Grid>
You may need to adjust your dragging logic to handle dragging the Grid instead of the Ellipse itself.
I've found a very easy way to do this in plain XAML: set Margin="-1000000". Read more here: Positioning an element inside the Canvas by its center (instead of the top left corner) using only XAML in WPF

Resources