ExpanderView will not expand from inside XAML - xaml

Previously I ported ExpanderView from Windows Phone toolkit to WinRT ExpanderRT, just to notice now that if you have two ExpanderView controls inside a StackPanel or ListView and you want the first expanderView to be expanded from the beginning by setting the IsExpanded property to True, then first expanderView will overlay the second one.
Here is an example:-
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<local:ExpanderControl
IsExpanded="True"
Expander="This is the expander">
<local:ExpanderControl.Items>
<Button Content="Yes"/>
<Button Content="No"/>
</local:ExpanderControl.Items>
</local:ExpanderControl>
<local:ExpanderControl
IsExpanded="False"
Expander="This is the expander">
<ListViewItem>
<StackPanel Orientation="Horizontal">
<Button Content="yes"/>
<Button Content="no"/>
</StackPanel>
</ListViewItem>
</local:ExpanderControl>
</StackPanel>
</Grid>
After few hours trying to debug the ExpanderView control code i found out that this code is firing 4 times
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
if (_presenter == null) return;
var parent = _presenter.GetParentByType<ExpanderControl>();
var gt = parent.TransformToVisual(_presenter);
var childToParentCoordinates = gt.TransformPoint(new Point(0, 0));
_presenter.Width = parent.RenderSize.Width + childToParentCoordinates.X;
}
private void OnPresenterSizeChanged(object sender, SizeChangedEventArgs e)
{
if (null != _itemsCanvas && null != _presenter && IsExpanded)
{
_itemsCanvas.Height = _presenter.DesiredSize.Height;
}
}
During the first 2 times, the _itemsCanvas has a height of 0. While the third time it has a height of 64 just to be overwriten to 0 by the forth time.
I have not find any reason why this is happening. Anyone here can help?

I have faced similar issues after porting Expander from windows phone toolkit.
To fix this issue, I modified OnPresenterSizeChanged logic
private void OnPresenterSizeChanged(object sender, SizeChangedEventArgs e)
{
if (null != _itemsCanvas && null != _presenter && IsExpanded)
{
if (double.IsNaN(_itemsCanvas.Height))
{
VisualStateManager.GoToState(this, CollapsedState, false);
UpdateVisualState(true);
}
else
{
// Already expanded, so we need to update the height of the canvas directly.
_itemsCanvas.Height = _presenter.DesiredSize.Height;
}
}
}
What is different here is that I check if the item canvas has been rendered before or not based on checking if it's height is Nan, if that's the case then I change visual state to collapse without transition then I call UpdateVisualState(true). else I just update the render height of canvas.
The issue was that the first time UpdateVisualState was called, the content presnter was null.

Related

UWP Offset GridView Scrollbar

I am making a Gridview that scrolls behind a title element (which is semi transparent to show the items being scrolled behind it). To do this, I have layered the Grid containing the title and the GridView by placing them both as children in the same Grid.
<Grid>
<GridView>
<!-- Stuff -->
<GridView>
<Grid Height="100">
<!-- Title Content here -->
</Grid>
</Grid>
This works fine, but causes the GridView to display elements initially behind the title. To fix this, I offset the ItemsWrapGrid in the GridView:
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Name="ItemsWrapGrid"
Margin="0,100,0,0"
Orientation="Horizontal"
HorizontalAlignment="Center"></ItemsWrapGrid>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
Now the items start as if they are below the title content, and scroll underneath it.
The only remaining problem is the scrollbar for the GridView. The scrollbar still goes to the top of the parent Grid, which means it goes behind the title, even though the items in the GridView themselves begin below the title. This is particularly an issue when there are enough items in the GridView (rows) to cause the scrollbar to be small enough that it is entirely behind the title.
Is there a way to offset the scrollbar similar to the GridView items? Is this the wrong approach?
UWP Offset GridView Scrollbar
For your requirement, you could use VisualTreeHelper to get VerticalScrollBar element, then set Margin = 0,100,0,0 in the GridView load event handler. For detail steps please refer the following code.
public static DependencyObject MyFindGridViewChildByName(DependencyObject parant, string ControlName)
{
int count = VisualTreeHelper.GetChildrenCount(parant);
for (int i = 0; i < count; i++)
{
var MyChild = VisualTreeHelper.GetChild(parant, i);
if (MyChild is FrameworkElement && ((FrameworkElement)MyChild).Name == ControlName)
return MyChild;
var FindResult = MyFindGridViewChildByName(MyChild, ControlName);
if (FindResult != null)
return FindResult;
}
return null;
}
private void TestGridView_Loaded(object sender, RoutedEventArgs e)
{
var scrollBar = MyFindGridViewChildByName(TestGridView, "VerticalScrollBar");
scrollBar.SetValue(MarginProperty, new Thickness(0, 100, 0, 0));
}

UWP Binding: Changing backgrounds in XAML using C#

Suppose I am making a simple UWP application which navigates through several pages. I would like to have a common background for all pages, depending on which background a user has selected from the Settings page.
I have a SettingsPage.xaml with a comboBox (and Grid Background that needs to change):
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ComboBox Name="ColourSelect" SelectionChanged="ComboBox_SelectionChanged">
<ComboBoxItem Name="Red">Red</ComboBoxItem>
<ComboBoxItem Name="Green">Green</ComboBoxItem>
<ComboBoxItem Name="Blue">Blue</ComboBoxItem>
</ComboBox>
</Grid>
Which interfaces with my SettingsPage.xaml.cs file:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Change background
if (Red.IsSelected) { } // Change to Red.png
else if (Green.IsSelected) { } // Change to Green.png
else if (Blue.IsSelected) { } // Change to Blue.png
}
I have set up my App.xaml to contain a background resource, but I'm not sure how to bind it to the C# in Settings.xaml.cs.
<Application.Resources>
<Style TargetType="Grid" x:Key="CommonBackground">
<Setter Property="Background" Value="{ <!-- Some image. How to bind? --> }"
</Style>
</Application.Resources>
What should I return to bind the user decision to the Application resources?
Thank you in advance!
This requires few changes in different pieces of application. Follow my steps.
In this case I a am creating two Resources. One that will maintain the Settings Combobox Colour Scheme. Second one is BitMapImage in Resource.
So my Application.Resource will look something like below.
<Application.Resources>
<image:BitmapImage x:Key="BackgroundSource" UriSource="ms-appx:///Assets/Red.png" />
<x:String x:Key="BackgroundBrush">Red</x:String>
</Application.Resources>
Make sure you are adding xmlns:image="using:Windows.UI.Xaml.Media.Imaging" in your App.xaml.
Now Create a Static Method inside App.xaml.cs that will be used to update Background to the Page during Run time. It should be something like below.
public static void UpdateBGColors(string Color)
{
switch (Color)
{
case "Red":
Current.Resources["BackgroundSource"] = "ms-appx:///Assets/Red.png";
break;
case "Green":
Current.Resources["BackgroundSource"] = "ms-appx:///Assets/Green.png";
break;
case "Blue":
Current.Resources["BackgroundSource"] = "ms-appx:///Assets/Blue.png";
break;
default:
Current.Resources["BackgroundSource"] = "ms-appx:///Assets/Red.png";
break;
}
}
Now Your combobox_SelectionChanged should look like below.
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox cb = sender as ComboBox;
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
localSettings.Values["BackgroundBrush"] = (cb.SelectedValue as ComboBoxItem).Content;
App.UpdateBGColors((cb.SelectedValue as ComboBoxItem).Content.ToString());
}
Now you need to wire up the Background of each page to the Resource BackgroundSource. So anywhere you want the background to be set based on settings add below lines of code
<Grid>
<Grid.Background>
<ImageBrush ImageSource="{StaticResource BackgroundSource}" />
</Grid.Background>
......
</Grid>
At this point, if you change the setting in setting page and if you navigate back to original page that you came into setting page, The background should be set automatically to whatever you selected in Settings.
But you also want to make sure the same background is loaded when the app is opened next time. To do that in App.xaml.cs, Add below lines in the beginning of OnLaunched Event.
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
if (localSettings.Values["BackgroundBrush"] != null)
{
UpdateBGColors(localSettings.Values["BackgroundBrush"].ToString());
}
Since in settings page, you are saving BackgroundBrush Everytime you change the Combobox Item, Whenever your app is loading, Based on the BackgroundBrush BackgroundSource will be assigned to correct Uri and will be used as Page Backhground.
Full Repo is available Here
Good Luck.
[Update] You can use this, and after save your settings.
SettingsPage.xaml
<Grid>
<Grid.Background>
<ImageBrush x:Name="colorImage" Stretch="UniformToFill"/>
</Grid.Background>
<ComboBox Name="ColourSelect" SelectionChanged="ComboBox_SelectionChanged">
<ComboBoxItem Name="Red">Red</ComboBoxItem>
<ComboBoxItem Name="Green">Green</ComboBoxItem>
<ComboBoxItem Name="Blue">Blue</ComboBoxItem>
</ComboBox>
</Grid>
SettingsPage.xaml.cs
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (Red.IsSelected)
{
ChangeColorImage("ms-appx:///Assets/Red.png");
}
else if (Green.IsSelected)
{
ChangeColorImage("ms-appx:///Assets/Green.png");
}
else if (Blue.IsSelected)
{
ChangeColorImage("ms-appx:///Assets/Blue.png");
}
}
private void ChangeColorImage(string imageUrl)
{
// using Windows.UI.Xaml.Media.Imaging;
BitmapImage imageSource = new BitmapImage(new Uri(imageUrl));
colorImage.ImageSource = imageSource;
}

Listview and Drag Item Windows Phone 8.1

I'm scruiggling with Windows Phone behavior. And maybe you could help me out somehow.
I want to have a listview from which I can drag items to another Gridview.
So far I got dragging enabled by setting ReorderMode = "Enabled". Doing this has some drawbacks.
1. I'm not able to scroll in my listview anymore
2. I can't select items anymore
3. I don't want the items to be reordered
What I want to have:
1. When holding an item, I want to drag this to another gridview
2. I want still be able to scroll in the listview
3. I still want to be able to select items
Is that somehow possible to do in Windows Phone 8.1?! Can I do my own dragging? Is yes, how should I start?!
Many thanks for any advise
ReorderMode isn't want you want in this case. Here's some basic functionality to do this between two ListViews:
<StackPanel Orientation="Horizontal" Width="800">
<ListView x:Name="ListView1" HorizontalAlignment="Left" DragItemsStarting="ListView_DragItemsStarting" AllowDrop="True" CanDragItems="True" CanReorderItems="True" Drop="ListView_Drop"/>
<ListView x:Name="ListView2" HorizontalAlignment="Right" DragItemsStarting="ListView_DragItemsStarting" AllowDrop="True" CanDragItems="True" CanReorderItems="True" Drop="ListView_Drop"/>
</StackPanel>
ObservableCollection<string> AlphabetList;
ObservableCollection<string> NumberList;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
AlphabetList = new ObservableCollection<string>();
AlphabetList.Add("A");
AlphabetList.Add("B");
AlphabetList.Add("C");
AlphabetList.Add("D");
AlphabetList.Add("E");
AlphabetList.Add("F");
AlphabetList.Add("G");
AlphabetList.Add("H");
AlphabetList.Add("I");
AlphabetList.Add("J");
ListView1.ItemsSource = AlphabetList;
NumberList = new ObservableCollection<string>();
NumberList.Add("0");
NumberList.Add("1");
NumberList.Add("2");
NumberList.Add("3");
NumberList.Add("4");
NumberList.Add("5");
NumberList.Add("6");
NumberList.Add("7");
NumberList.Add("8");
NumberList.Add("9");
ListView2.ItemsSource = NumberList;
}
IList<object> DraggedItems;
private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
DraggedItems = e.Items;
}
private void ListView_Drop(object sender, DragEventArgs e)
{
ListView ThisListView = sender as ListView;
ObservableCollection<string> AddingOC = (ThisListView.Name == "ListView1" ? AlphabetList :NumberList);
ObservableCollection<string> RemovingOC = (ThisListView.Name == "ListView1" ? NumberList : AlphabetList);
if (AddingOC.Contains(DraggedItems[0])) return;
foreach (string O in DraggedItems)
{
RemovingOC.Remove(O);
AddingOC.Add(O);
}
}

How to access parent page controls in child page(User control)in windows metro apps?

I need to access the mainpage values in usercontrols (child page) in windows 8 metro apps.Please let me know how to do this?
In child page i am trying to access the mainpage controls like this :
DealersPage main = new DealersPage();
Grid grd = main.FindName("visibleGrid") as Grid;
grd.Visibility = Visibility.Collapsed;
But no luck !!!Please tell me
EDIT:In Child Page(usercontrol) on image tapped event i wrote below code.
private void imgClose_Tapped(object sender, TappedRoutedEventArgs e)
{
DealersPage main = this.Parent as DealersPage;
if (main != null)
{
Grid grd = main.FindName("visibleGrid") as Grid;
grd.Visibility = Visibility.Collapsed;
}
settingsControl.Visibility = Visibility.Collapsed;
}
in MainPage (DealersPage) my design like this:
<Grid HorizontalAlignment="Left" Height="758" Margin="0,10,0,0" VerticalAlignment="Top" Width="1366" x:Name="visibleGrid" Opacity="0.85" Background="#FF858585">
<Grid HorizontalAlignment="Left" Height="600" Margin="454,108,0,0" Visibility="Collapsed" VerticalAlignment="Top" Width="500" x:Name="gridpopup">
<local:Settings x:Name="SettingsUsercontrol" Visibility="Collapsed" Height="600" Width="500"/>
</Grid>
</Grid>
From your code, you are NOT actually accessing the main page control in which the user control is present. By using new DealersPage() you are actually creating a new instance of the DealersPage (your main page) on which FindName might return the grid; but that will never be the one that you actually would be looking for.
Your main should be something like (DealersPage)this.Parent which would be the actual parent page.
CODE: Somewhere in your UserControl -
DealersPage main = this.Parent as DealersPage;
if(main != null)
{
Grid grd = main.FindName("visibleGrid") as Grid;
grd.Visibility = Visibility.Collapsed;
}

Image rotation as animation

I am making a Windows 8 application in visual studio 2012 c#.
I am having an image '1.png' and I want to rotate it at any angle as an animation along its center point.
But i want to do it with the help of c# code rather than XAML code.
Thank You in Advance.
In your XAML, have the following image:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Image Source="/Assets/Logo.png" Width="300" RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<RotateTransform x:Name="rotateTransform"/>
</Image.RenderTransform>
</Image>
</Grid>
Then, in code, write the following when you want to animate (you create the Storyboard programmatically, then add to it a relevant Timeline. Note that you can also create the RotateTransform in code if you want.
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(500);
Storyboard board = new Storyboard();
var timeline = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(timeline, rotateTransform);
Storyboard.SetTargetProperty(timeline, "Angle");
var frame = new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromSeconds(1), Value = 360, EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut } };
timeline.KeyFrames.Add(frame);
board.Children.Add(timeline);
board.Begin();
}
This will rotate the object 360 degrees.
BTW: I am writing a set of posts that show an even better way of animating. It's not done yet, but it will give you a general idea on how to get a framework for certain types of animations..
First part of the series
Thanks Shahar! I took your example and made a custom control. It's actually an infinite spinning of one ring image.
Spinner.xaml:
<UserControl x:Class="MyControls.Spinner"
...
<Grid >
<Image Source="/Assets/Images/spinner.png" Width="194" RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<RotateTransform x:Name="rotateTransform"/>
</Image.RenderTransform>
</Image>
</Grid>
</UserControl>
Spinner.cs:
namespace MyControls
{
public partial class Spinner: UserControl
{
public Spinner()
{
InitializeComponent();
this.Loaded += Spinner_Loaded;
}
private void PlayRotation()
{
Storyboard board = new Storyboard();
var timeline = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(timeline, rotateTransform);
Storyboard.SetTargetProperty(timeline, new PropertyPath("(Angle)"));
var frame = new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromSeconds(5), Value = 360, EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut } };
timeline.KeyFrames.Add(frame);
board.Children.Add(timeline);
board.RepeatBehavior = RepeatBehavior.Forever;
board.Begin();
}
private async void Spinner_Loaded(object sender, RoutedEventArgs e)
{
PlayRotation();
}
}
}
Then when you want to use Spinner in another xaml, it's very simple:
Just add a line for it inside any Grid etc:
<include:Spinner/>
(of course you need to define include as something like:
xmlns:include="MyControls"
on top of your xaml)