Lets say that I have a grid with 2 rows, 2 columns and many controls inside each cell.
When the application is changed to snap mode, I meant 1/3 of the screen I would like the application to be only, one Column, 2 rows and show only some controls I would decide.
What kind of control do I have for this?
thx
You should make use of the VisualStateManager in xaml, for a full xaml solution:
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="OrientationStates">
<VisualState x:Name="Full"/>
<VisualState x:Name="Fill"/>
<VisualState x:Name="Portrait"/>
<VisualState x:Name="Snapped"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
Create StoryBoards for each VisualState and hide/show elements in your xaml. Microsoft examples use the same solution.
--
Update
I searched the net and found the proper states, an example is behind this link: MSDN.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<VisualState x:Name="FullScreenPortrait"/>
<VisualState x:Name="Snapped"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
The states reflect the ApplicationViewState enum. Even more information can be found here.
Using DisplayProperties.OrientationChanged event (as suggested by #Jan K.) may not be exactly what you are looking for, considering the remarks section to this event:
The DisplayProperties.OrientationChanged event occurs only when orientation of the display or monitor changes and not necessarily when the orientation of your app changes. To determine the orientation of your app for layout purposes, use the ApplicationView.Value property.
but since ApplicationView.Value probably will be abandoned after Windows 8.1 release MS suggest to use ApplicationView.GetForCurrentView() instead:
ApplicationView static methods may be altered or unavailable for releases after Windows 8.1 Preview. Instead, use ApplicationView.GetForCurrentView() to get an instance of ApplicationView.
So for now I've end up with that code (have a kind of dynamic view and can't pre-design everything in XAML via VisualStateManager, unfortunately):
public MainPage()
{
InitializeComponent();
Window.Current.SizeChanged += (sender, args) =>
{
ApplicationView currentView = ApplicationView.GetForCurrentView();
if (currentView.Orientation == ApplicationViewOrientation.Landscape)
{
// when args.Size.Width > args.Size.Height
}
else if (currentView.Orientation == ApplicationViewOrientation.Portrait)
{
// when args.Size.Width < args.Size.Height
}
};
}
Have a look on the DisplayProperties.OrientationChanged-Event. When it fires you can modify your grid and rearrange your controls.
Related
This is basically simple UI that would conditionally render a label with text Triggered - plain in case the ShowContent property of binding is True. (Overly simplified example here but it works and I can see the label toggles).
<Grid>
<Button Click="ChangeState"/>
<ContentView>
<ContentView.Style>
<Style TargetType="ContentView">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowContent}" Value="True" TargetType="ContentView">
<Setter Property="ControlTemplate">
<Setter.Value>
<ControlTemplate>
<Label>Triggered - Plain</Label>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentView.Style>
</ContentView>
</Grid>
There is also a button that would toggle the state of the Grid:
public void ChangeState(object sender, EventArgs e){
this.state = !this.state; // toggle
if(this.state){
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Selected);
} else {
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Normal);
}
}
Problem
I am not sure how to apply a different style to the label based on selected state.
If you use VisualStateManager, you need to have a name on the Label. I want to apply selected style in a blanket way on all inner labels.
Also, if we target the label with a name, when the Label is not on the UI (due to state ShowContent being False), GoToState fails with null exception as it cannot find the label.
The best solution seems to be using CSS but that does not support defined colors and dynamic resources (AFAIK).
Any idea what to do?
Update: one possible solution is to apply the state change to all inner elements:
private IList<T> FindAllChildren<T>()
where T : IVisualTreeElement
{
return this.GetVisualTreeDescendants()
.Where(e => e is T)
.Cast<T>()
.ToList();
}
private void ApplyState(string state)
{
VisualStateManager.GoToState(this, state);
FindAllChildren<VisualElement>().ForEach(e => VisualStateManager.GoToState(e, state));
}
public void ChangeState(object sender, EventArgs e){
this.state = !this.state; // toggle
if(this.state){
ApplyState(VisualStateManager.CommonStates.Selected);
} else {
ApplyState(VisualStateManager.CommonStates.Normal);
}
}
You still need to create VisualStateGroup styling for the labels and give labels a specific style/class:
<label class="Selectable">...</label>
<Style class="Selectable" TargetType="Label">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup Name="all">
<VisualState Name="Normal">
<VisualState.Setters>
<Setter Property="Label.TextColor" Value="{DynamicResource Normal_Color}"/>
</VisualState.Setters>
</VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="Label.TextColor" Value="{StaticResource Selected_Color}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
I recommend that you work around this. Too many bugs, and too different behavior on the different platforms for containers.
Fixing the visual state is one thing. Then you need to fix "IsEnabled" for child problem. After that if you change the visibility, you will notice that on IOS it is doing one thing, on android - another. (You will start losing this visual state from time to time). At some point you will start looking for ways to force the page to redraw itself.
My advice is, for now, give up on this idea. Until those problems are solved. Wasted too many hours trying to make this work for all platforms.
(Some of the issues are 6+ months old, and they keep pushing them to backlog.)
This is me, asking the same thing, a month ago: Pass the VisualState of CollectionView Item VisualElement to its child VisualElements
Edit: So, what work arounds I use.
Besides styles, visual states, data triggers?
ControlTemplates and Messages between ViewModel <-> View.
Control templates are reusable pieces of user interface, and there isn't much you have to do. You can make all VisualElements bind to the same thing, using TemplatedParent as BindingContext of the container.
Messages I use for some sorts of animations (And other special requests). You can in the ViewModel generate a message, that will be handled (or not) by the View. You have very good control over your View, but you do not break MVVM by coupling them.
A Warning: Every work around is parasitic code (you do something the wrong way, because someone else has been doing his job the wrong way). That code sooner or later will have to be deleted/replaced. Mark it with TODO, because it may take huge part of your app, and later it will be hard to find out all usage places. For now test on IOS. It takes much less work to make it work on IOS, then fix Android, than the other way around.
AdaptiveTrigger with MinWindowWidth=2160 doesn't seems to work. I need it to handle Microsoft Surface Pro 3 screen resolution (2160x1440).
Look at this simple code below:
<Page
x:Class="TestUWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestUWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="2160" d:DesignHeight="1440">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="2160" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="brdMain.Background" Value="#bbbbbb"></Setter>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="brdMain.Background" Value="#303030"></Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="brdMain">
<TextBlock Text="Testing"></TextBlock>
</Border>
</Grid>
</Page>
You will see, the background colour is always black (#303030). Is there a maximum Width that VisualState can handle? Any idea?
Thanks
You have to remember that measurements in UWP are done in effective pixels (epx). See MSDN. Surface Pro 3, like other Surface tablets, has HiDPI display and a default scale factor greater than 1 which means that its effective pixel resolution is smaller than 2160x1440 even though that is its native resolution.
The SP3's default scale factor is 150%, resulting in an epx resolution of 1440x960. So even if you maximize your window, the window width is only at most 1440 epx, which means the MinWindowWidth="2160" state trigger will never fire on an SP3 with default settings.
If you want your state trigger to fire only on tablets with HiDPI displays and/or a certain native resolution, you will probably need to implement a custom state trigger that detects all of these conditions. How you do this is beyond the scope of this question.
I think your sizing might be off. Have you tried any others?
According to the Official MSDN Screen Sizes and Layouts Documentation these are the sizes that you want to use
The reason you probably don't want the exact screen size is because what's stopping someone from adjusting it down a little or up a little?
Personally, for more complex layouts, I prefer to create separate views for each size. It gives me more control over the layout. Here's how I use it.
In a static application level class I have.
public enum DeviceType
{
Desktop = 0,
Phablet = 1,
Mobile = 2
}
public static DeviceType CurrentDevice
{
get
{
ApplicationView view = ApplicationView.GetForCurrentView();
Rect rect = view.VisibleBounds;
if (rect.Width >= 1024)
{
return DeviceType.Desktop;
}
else if (rect.Width >= 720)
{
return DeviceType.Phablet;
}
else
{
return DeviceType.Mobile;
}
}
}
Then in my control I just access my static class in my Static Constructor. If I am a mobile device I load a mobile DefaultStyleKey. If I am desktop then I load a DesktopDefaultStyleKey.
DeviceType device = ApplicationServices.CurrentDevice;
switch (device)
{
case (DeviceType.Desktop):
YoutubeVideosPresenter.Content = new YouTubeVideosLayoutDesktop();
break;
case (DeviceType.Mobile):
YoutubeVideosPresenter.Content = new YouTubeVideosLayoutMobile();
break;
}
Of course this is not very "adaptive" if someone manipulates the window width. You can easily get past this though by checking to see if the window width has changed and then your style can easily be switched out.
How create own animation when item add or remove from GridView? For example change colour from dark to light.
If Item is a Grid:
<Grid.Transitions>
--> There can be only predefinied *ThemeTransitions?
</Grid.Transitions>
Is other way to do this?
Tim is correct that the Transitions are pre-defined at this point. However, you should be able to achieve your scenario using Storyboard. There are probably several ways to do this, e.g. retemplating GridViewItem and adding new "Loading"/"Unloading" visual states. Here is a simple way to achieve your scenario by putting a Storyboard in the ItemTemplate:
MainPage.xaml:
<GridView x:Name="MyGV">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Loaded="Grid_Loaded" x:Name="TemplateRoot" Opacity="0" Background="White">
<Grid.Resources>
<Storyboard x:Key="LoadedStoryboard">
<DoubleAnimation Storyboard.TargetName="TemplateRoot"
Storyboard.TargetProperty="Opacity"
BeginTime="0:0:1"
Duration="0:0:5"
To="1" />
</Storyboard>
</Grid.Resources>
<TextBlock Text="{Binding}" FontSize="24" Foreground="Black" Margin="40" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
MainPage.xaml.cs:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Storyboard sb = ((Grid)sender).Resources["LoadedStoryboard"] as Storyboard;
sb.Begin();
}
Example source code is hosted here:
https://github.com/finnigantime/Samples/tree/master/examples/Win8Xaml/GridViewItemLoadedUnloadedAnimations
What if the set that is used as the GridView's ItemsSource was an ObservableCollection and your code behind listened for changes from that collection? Then from the code behind you could control animations.
Correct, there can only be pre-defined transitions. The transition model is not exposed publically at this time.
The rows added to the "parent" ListView of a GridView could be fed in several ways, but often are bound to a ObservableCollection of something.
The columns of the GridView are governed with a ObservableCollection, as well, so the context should be pretty similar.
I published an article on how to manage the columns' management (with animation) just some days ago. Perhaps could help you.
http://highfieldtales.wordpress.com/2013/08/08/hacking-the-wpf-gridview-adding-the-animation/
UPDATE: pardon me, but I realized later that you meant the XAML for Store apps. My reference is for WPF, instead.
I'm having trouble performing a simple storyboard-based animation of a controls height in a Metro-style C#/XAML application on Windows 8.
The following trivial XAML and code behind snippets work fine in Silverlight 5 and Windows Phone 7, yet do nothing in Windows 8 (at least for me):
<Page.Resources>
<Storyboard x:Name="expandAnimation">
<DoubleAnimation Storyboard.TargetName="scaleButton" Storyboard.TargetProperty="Height" From="50" To="200" Duration="0:0:1"/>
</Storyboard>
</Page.Resources>
<StackPanel Width="200">
<Button x:Name="scaleButton" Click="scaleButton_Click" Content="Scale"/>
<Button Content="Another button"/>
<Button Content="Yet another button"/>
</StackPanel>
C# code:
private void scaleButton_Click(object sender, RoutedEventArgs e)
{
expandAnimation.Begin();
}
The same code can be altered to animate other properties of the control such as Opacity which works as expected.
I can animate a ScaleTransform to do the scaling, but it alters the internal rendering of the control, and does not affect the layout of neighbouring controls which is a problem for me.
Hopefully I'm not missing anything obvious here, but shouldn't this just work?
You just need to add EnableDependentAnimation="True" and then it should work fine.
A dependent animation is one that will cause the Xaml to re-layout. Expensive; therefore requiring an "opt-in".
http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.ui.xaml.media.animation.pointanimation.enabledependentanimation.aspx
If possible you should use a render transform and scale the element's visual instead. This is independent meaning that the rest of the elements on the page will not need to move to accommodate.
Is there a way to do something like the following?
<Style TargetType="{x:Type: TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="~Complex Binding~" Value="True" DoNotUnset="True">
<Setter Property="IsExpanded" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
What I basically would like is this to be a "No Undo DataTrigger" if you will. When the Value is no longer "True" I don't want it to set "IsExpanded" back to its previous value.
Here is my attempt to do this using enter actions but this also has problems.
<Style TargetType="{x:Type: TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="~Complex Binding~" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(IsExpanded)" Duration="00:00:01" FillBehavior="Stop">
<BooleanKeyFrameCollection>
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True"/>
</BooleanKeyFrameCollection>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
First this is insanely verbose but secondly this only works for the duration of the BooleanAnimationUsingKeyFrames. If I change the FillBehavior to "HoldEnd" then it looks correct but now the user can no longer un-expand the treeviewitem by clicks (though oddly enough they still can by using the keyboard).
For those who are wondering: yes I'm trying to expand all items in a tree view by binding because I don't want to build a recursive ItemsGenerator.GetItemContainerFromIndex(i) loop. I'd still like to use a similar "No Undo Datatigger" in other areas of my code.
I would bind the ~Complex Binding~ to a bool in the ViewModel which once set to true is always true.
private bool _onceTrueAlwaysTrue = false;
public bool OnceTrueAlwaysTrue
{
get
{
return _onceTrueAlwaysTrue;
}
set
{
if(value)
{
_onceTrueAlwaysTrue = true;
OnPropertyChanged("OnceTrueAlwaysTrue");
}
}
}
then bind this property to the IsEnabled and you should be fine. If you want to reset it simply make a reset method that sets _onceTrueAlwaysTrue = false;
As for the NoUndo datatrigger, as far as I know there is no such thing. You are gonna have to do some kind of work around every time.
The properties changed by triggers are automatically reset to their previous value when the triggered condition is no longer satisfied. Triggers are optimized for transient states which are expected to change and return to original state, such as IsPressed on Button and IsSelected on ListBoxItem.