UWP ContentControl not applying ContentThemeTransition - c#

I'm trying to get a ContentControl to apply a ContentThemeTransitionwhere the content will be a string, so when the string changes via a binding the new string will animate in. I can't use ContentThemeTransition with a TextBlock as this doesn't derive from ContentControl.
Here is some example XAML that shows the problem. If I edit the text in the Textbox (which represents the text in my ViewModel that the ContentControl is really bound to) I would expect the Text shown in the ContentControl to animate in, but it doesn't.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" x:Name="text" Text="Hello" Width="100" Height="30"/>
<ContentControl Grid.Row="1" Width="100" Height="100" Content="{Binding ElementName=text , Path=Text}">
<ContentControl.Transitions>
<TransitionCollection>
<ContentThemeTransition HorizontalOffset="40"/>
</TransitionCollection>
</ContentControl.Transitions>
</ContentControl>
</Grid>
What am I doing wrong?
Update
I am getting somewhere now. If you replace the ContentControl XAML to
<ContentPresenter Background="Black" Foreground="Red" Grid.Row="1" Width="100" Height="100" Content="{Binding ElementName=text , Path=Text}">
<ContentPresenter.ContentTransitions>
<TransitionCollection>
<ContentThemeTransition VerticalOffset="-100"/>
</TransitionCollection>
</ContentPresenter.ContentTransitions>
</ContentPresenter>
then it works. Oddly, with the VerticalOffset =-100 as above the new value animates down outside of the ContentControli.e. its visible outside the bounds of the control. Anyone know how to change things so the animation only appears within the confines of the ContentPresenter?

Tested by my side, your first <ContentThemeTransition HorizontalOffset="40"/> and second <ContentThemeTransition VerticalOffset="-100"/> both work but only for the first time, you can enlarge the HorizontalOffset for testing and check if it works.
Since you expected that the animation should work every time the text is changed, I think you will need to create a animation which targets the Text of TextBlock directly instead of targeting the Content of ContentControl.
In this scenario, XAML Behaviors will be a good helper. You can refer to #Jerry Nixon - MSFT's answer in thread: How to animate TextBlock when its value changes in WinRT XAML?
Update:
My mistake that I only noticed that you changed HorizontalOffset to VerticalOffset. You actually also changed ContentControl to ContentPresenter.
According to the UI coordinate of UWP, since your animation target the ContentPresenter, then consider the left-top point of your ContentPresent is (0, 0). When you set <ContentThemeTransition VerticalOffset="-100"/>, it will transit from up (0, -100) back to (0, 0) and it will definitely animate outside of your ContentPresent, I think it is designed to be so, and we're not able change it. Here I can only suggest that modifying the transition from down to up like <ContentThemeTransition VerticalOffset="100"/>, it will help a little here but eventually it changes your animation, so I don't think this will be a good approach.

Related

How can I make a ScrollViewer properly size dynamically with a StackPanel?

For reference, this is a chat application. This should give you some idea of a final goal.
Additionally, I am very new to WPF. This is one of my first applications and I am making this as a proof of concept. I've been using Windows Forms up until this point, so any comparison or reference to it would help me understand a bit better.
So, the issue at hand:
The chat box for my chat application is a StackPanel (should it be?) which is programmatically populated with TextBlock elements. I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise).
The question: How can I make a ScrollViewer properly size dynamically with a StackPanel?
Additionally, I also need this StackPanel to size dynamically as the window is sized. This, in turn, would affect the scroll bar.
My current "solution" is to use a ScrollViewer with the StackPanel nested. However, the ScrollViewer and StackPanel do not size properly with a change in window size, as shown in screenshot #2. The XAML code and a screenshot of the designer is shown below.
<Window x:Name="Main" x:Class="dprCxUiDemoWpf.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:dprCxUiDemoWpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Background="#FF171717">
<TextBox x:Name="ChatBox" TextWrapping="Wrap" Background="#FF4F4F4F" Foreground="White" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" RenderTransformOrigin="-0.118,12.093" Margin="146,0,0,1" VerticalAlignment="Bottom" Height="46" BorderBrush="#FFFF00F3" KeyDown="ChatBox_KeyDown"/>
<Image x:Name="DprLogo" Source="/dprCxUiDemoWpf;component/Images/logo1.png" HorizontalAlignment="Left" Height="60" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="123"/>
<ScrollViewer Background="Red" Height="Auto" Width="Auto" ScrollViewer.CanContentScroll="True" Margin="146,0,0,0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" VerticalAlignment="Top" MinHeight="372">
<StackPanel x:Name="ChatPanel" Height="Auto" Width="Auto" Background="DimGray" ScrollViewer.CanContentScroll="True" />
</ScrollViewer>
</Grid>
</Window>
(source: gcurtiss.dev)
Please note the following regarding the first screenshot:
A. The black column (containing the logo) is simply the background color of the window; there is nothing there.
B. The gray portion is ChatBox (the StackPanel)
C. The pink highlighted box below is the text box where messages are entered.
I appreciate and accept any and all help!
You have to use the Grid panel properly. You layout its children by defining rows and columns. Grid is a column/row based layout container. Then configure row and column definitions to control the resize behavior of the cells and their content.
Using absolute positioning and sizes will of course prevent controls from responding to their parent's size changes. Most control stretch to fit the available space. But this requires dimension properties being set to Auto.
You said you are "more of a hands-on learner", but you should still read some documentations. Otherwise you will significantly slow down your progress until stagnation.
There are tons of blogs waiting for you to read them. To poke around in the dark will get you nowhere. You need at least to know the basics. Instead of waiting 13+ hours for a copy & paste ready answer, you could have finished multiple tutorials already and solve this on your own. Success is a good feeling. This is a very trivial problem.
Find yourself a good tutorial that you find easy to understand and start to experiment with the Grid after reading it.
According to your posted code, you obviously have zero idea how this panel works and how you can use it to align your controls.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<StackPanel />
</ScrollViewer>
<Image Grid.Row="1" Grid.Column="0"
Height="60"
Width="123 />
<TextBox Grid.Row="1" Grid.Column="1" />
</Grid>
I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise)
You should read about data-binding and MVVM first. Usually you hold an ObservableCollection of items on your VM and bind them to eg a ListBox on your View.
Then you can scroll-down the ListBox, each time a new item got added to your collection:
private void ScrollRecordsListToBottom()
{
if (RecordsList.Items.Count == 0)
return;
var lastItem = RecordsList.Items[RecordsList.Items.Count - 1];
RecordsList.ScrollIntoView(lastItem);
}

How can I force a WPF TextBox to both fill the content space, but also NOT grow beyond the bounds of it's container?

I need to create a simple user control which is used for displaying a descriptive message to the user. The XAML definition that I have for this control is as follows:
<UserControl x:Class="Console.WPF.DisplayMessageView"
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:DesignWidth="550"
HorizontalAlignment="Stretch">
<UserControl.Resources>
<!--Converters-->
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox
Grid.Column="0"
VerticalAlignment="Top"
FontWeight="Normal"
FontSize="10"
Padding="12,4,2,2"
Height="74"
Background="White"
BorderThickness="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
TextWrapping="Wrap"
Text="{Binding DetailsText, Mode=OneWay}" />
<Image Source="..."
Stretch="None"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0,2,0,0"
Visibility="{Binding IsErrorMessage, Converter={StaticResource BooleanToVisibility}}"/>
</Grid>
</UserControl>
This UserControl is currently embedded within a sizable stack of containers (DockPanels, StackPanels, etc.) and user controls. However, it's first parent container is a DockPanel. It is the last control in the DockPanel and the LastChildFill property is set to true on the panel.
My problem is that when I use this user control, regardless of HorizontalAlignment settings, it alway sizes the user control to the size of the text field. This means that if the text field is short, the user control will be small and look odd since it's not filling it's respective content hole. If the text is a vary long block of descriptive text, it doesn't wrap and continues to expand and is hidden off the edge of the screen.
I can't figure out how to fix this. I also want to note that this application does not use scroll bars, which have been band from use, except on list boxes. I can't use scroll regions for this application, however, this message object absolutely must expand to fill the container but NOT expand any larger.
How can I accomplish this without constraining a fixed Width value on my user control instance? Instead, I need the user control to grow with the container, when the window grows as well.
NOTE
I'm adding the C# tag, just in case this has to involve a code-behind change. I really hope it's just a matter of setting the right set of magic-properties, which have eluded me, however, I'm not opposed to encoding a rock-solid code behind solution, so long as it specifically does what I need it to do.

Border for UI feedback using binding

.NET/C#/WPF noob here. I'm trying to give a textbox a border based on a variable I can bind to (variable stores results of validation). My first attempt looked like this:
<Border ... Visibility="{Binding ServerName.IsValid, Converter={StaticResource BoolToVisibility}}">
<TextBox ... />
</Border>
But this had the undesired side effect of hiding the textbox within it when visibility was hidden. After that I looked at DataTriggers, but it didn't seem like what I needed. Any ideas on how I can bind the border to that variable?
Thanks!
You can layer it:
<Grid>
<Border ... /> <!-- Bind as before -->
<TextBox Margin="5"/> <!-- Margin to not completely hide border below -->
</Grid>
(You can also overlay the border by switching the order if that is preferable)
You could use a DataTrigger as well but you would need to change the appearance rather than hiding it completely.

Resize WPF application

Have tried searching google but I'm struggling to find an answer to this.
I have a basic WPF application with a few controls on. When I maximise the application the controls on the screen stay the same size and I get a lot of wasted space.
Is there a way to get the controls to grow and shrink dynamically with the size of the main window?
Kind Regards
Ash
Don't set a fixed Height and Width properties for your controls.
Instead set, horizontal and vertical alignment to stretch. And make sure your controls are contained inside an appropriate layout panel.
For example-
Fixed size grid:
<Grid Background="Red" Width="50" Height="50"/>
Dynamically expending grid:
<Grid Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
what you need is not resize, it's called scaling (or zooming) ;-)
Read this article; the task is very trivial with WPF.
(used to be much more complicated in Windows Forms...).
UI Scaling (UI Zooming) with WPF
basically all you need to add to the XAML is this:
<ScaleTransform
CenterX="0" CenterY="0"
ScaleX="{Binding ElementName=uiScaleSlider,Path=Value}"
ScaleY="{Binding ElementName=uiScaleSlider,Path=Value}"
/>
after that you can use mouse wheel or a slider or any other way (like in your case detect form maximized), to modify the Value of the ScaleTransform.
Where there's a will, there's a way. You will have to do some work yourself however, WPF can't automagically decide for you exactly how you want the resizing to be done.
Some relevant sources:
Layout containers
Data Binding
You can use content decorator that can stretch and scale a single child to fill the available space - Viewbox .
http://www.wpftutorials.com/2011/04/wpf-viewbox.html
this is no problem. The behavior of your controls depends on the container you use and the Horizontal and Vertical Alignment. For Example, if you use a Grid, a TextBox and a Button with Horizontal and Vertical Alignment: Stretch and width and height auto, the Textfield and Button will grow and shrink dynamically.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<TextBox Margin="8,8,8,104.96" TextWrapping="Wrap" Text="TextBox" Height="auto" Width="auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Button Content="Button" Margin="8,0,8,8" VerticalAlignment="Bottom" Height="92.96" Width="auto" HorizontalAlignment="Stretch"/>
</Grid>
Use a resizable control like Grid and put all the controls in Rows/Columns. Also set HorizontalAlignment to stretch for each control.

How to show a scheme in a document viewer?

I'm trying to show in screen something like a grades scheme.
The first solution which I did was drawing some rows definitions and columns in a Grid, accumulating some stacks panel (inside there are some rectangles) and setting in the row. But I don't like that idea.
I was using several bucles and I'd like to show that in a document viewer. Althoug I can imagine I may create a scheme o something easier.
UPDATE 1:
At this moment, I've got a class which contains as properties:
SubjectName: string
IsCorrect: bool
And others properties but they don't matters right now. Then I have all in a List. I don't know if it's useful to user a LINQ function to group by SubjectName and get its average score:
Classify elements from a list to another classification with average
But I really have interest in the control.
Personally I would (assuming the data is in a database of some kind) host those rows in an ItemsControl of some kind. The ItemsControl would be bound to a collection of the Items.
If I had a little leeway, I would make it a plain old list box and customize the styling of the items). The bar chart portion at the end should be fairly easy to accomplish by having a rectangle whose width is bound to the grade property or whatever which is applied as a percentage of the width of the container it is in.
So I would have a Subject Class (which is created from the data source) with several properties :
Name (e.g. "Times Tables") Represented by a TextBlock
Grade (e.g. 52% ) Represented by a rectangle bound to a property which multiplies that value by the width of the grid in which the rectangle resides
Action (e.g. "Repeat") represented by another textblock.
I will post an example in a little while.
Example
OK so The window has a very simple layout, a Grid with a listbox in it. The listbox is bound to a design time datasource that I set up in Expression Blend.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewMFagic"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
x:Class="MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" MouseRightButtonDown="ShowContext" DataContext="{Binding Source={StaticResource dsSubjects}}">
<ListBox ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" HorizontalContentAlignment="Stretch"/>
</Grid>
</Window>
The ItemTemplate I put in the App.Xaml resource Library as I like keeping my Window and UserControl xaml files nice and clean and using resource dictionaries as much as possible to keep stuff out of the way. Anyhow, below is the ItemTemplate.
<DataTemplate x:Key="ItemTemplate">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.029*"/>
<ColumnDefinition Width="0.67*"/>
<ColumnDefinition Width="0.168*"/>
<ColumnDefinition Width="0.133*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding SubjectName}" VerticalAlignment="Bottom" d:LayoutOverrides="Width" Grid.ColumnSpan="1" Margin="0" Grid.Column="1"/>
<TextBlock Text="{Binding Action}" VerticalAlignment="Top" d:LayoutOverrides="Width, GridBox" Grid.ColumnSpan="1" Grid.Column="3" Margin="0"/>
<Border Grid.ColumnSpan="1" Grid.Column="2" Margin="0" d:LayoutOverrides="Height" Background="#A3000000" CornerRadius="5" Width="{Binding PercentCorrect}" HorizontalAlignment="Left" >
<TextBlock Text="{Binding PercentCorrect}" HorizontalAlignment="Center"/>
</Border>
<TextBlock TextWrapping="Wrap" Text="{Binding Number}" d:LayoutOverrides="Width, Height"/>
</Grid>
</DataTemplate>
And the finished product looks like this:
Now I cheated a little here. I bound the width of the border element for the grade directly to the percent. Given more time to mess with this, I probably would have made a ViewModel and had values for both the shaded part and the unshaded part which added up to 100%. Then bound column widths of a grid that I would put the border in to those values to have true percentage. Regardless, there is a starting point.

Categories