How to add ListBox Item Contextmenu in UWP - windows-10

I am searching to add Context menu in every item of my listbox item. I know it was very easy in wp8 app using toolkit. However, Toolkit is not supported in uwp.
How can I add Context menu in uwp listbox item?
Thanks!

You can create ListBox.ItemTemplate with MenuFlyout, for example:
<ListBox.ItemTemplate>
<DataTemplate>
<Grid PointerEntered="Grid_PointerEntered" >
<FlyoutBase.AttachedFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Name="EditButton"
Text="Edit"
Click="EditButton_Click"/>
<MenuFlyoutItem x:Name="DeleteButton"
Text="Delete"
Click="DeleteButton_Click"/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<TextBlock Text="{Binding Name}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
Handle the PointerEntered event to show Flyout when the pointer has been moved into an ListBoxItem:
private void Grid_PointerEntered(object sender, PointerRoutedEventArgs e)
{
FrameworkElement senderElement = sender as FrameworkElement;
FlyoutBase flyoutBase = FlyoutBase.GetAttachedFlyout(senderElement);
flyoutBase.ShowAt(senderElement);
}
Handle MenuFlyoutItem Click event:
private void EditButton_Click(object sender, RoutedEventArgs e)
{
var datacontext = (e.OriginalSource as FrameworkElement).DataContext;
//this datacontext is probably some object of some type T
}
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var datacontext = (e.OriginalSource as FrameworkElement).DataContext;
//this datacontext is probably some object of some type T
}
Please check my feasible sample on Github

Related

Display a popup with xamarin forms

I want to display a small popup in a xamarin forms application (iOS/Android PCL)
I am actually working with a ContentPage (XAML + C# code)
and i am showing this popup with:
await Navigation.PushModalAsync(mypopupinstance)
It works fine, but the popup is on the full screen. I just want a small popup and i want to see what is behind.
Thanks
You'll need to look elsewhere for this kind of functionality. One such library is Rotorgames' Popup plugin: https://github.com/rotorgames/Rg.Plugins.Popup
Modal pages can't be presented like that.
For small popups you can use
DisplayAlert()
Inside a Page.
If you want something more customizable just wrap the content of your Page Inside a relative Layout or grid, and add the popup on top of your normal content.
I am working on the same issue, so far I was able to create a popup which can hold a content page.
I am gladly willing to share my current state. Please note that I will keep the code examples as short as possible, therefore reducing it to a simple loading and a text prompt dialog.
Approach
After having used the Acr.UserDialogs library for a while, i felt the need of having dialogs I could customize due to my personal requirements. Also I wanted to minimize the necessity of having to rely on plugins.
Ideally such a dialog should be invoked with a simple call, for instance:
Dialogs.ShowLoading();
or
string result = Dialogs.ShowPrompt();
As things are with Xamarin.Forms it is quite obvious that we will require a dependency service implementation for that to work.
Shared Code Library
We create a basic interface "IDialogs.cs":
public interface IDialogs
{
bool IsDialogOpen();
void ShowLoading(LoadingDialog dialog);
void ShowPrompt(PromptDialog dialog);
void HideDialog();
}
Next thing to do is to have a static dialog class, which can be called from every page where a dialog is needed.
"Dialogs.cs":
public static class Dialogs
{
private static IDialogs dialogService = DependencyService.Get<IDialogs>();
public static bool IsDialogOpen()
{
return dialogService.IsDialogOpen();
}
public static void ShowLoading()
{
LoadingDialog dlg = new LoadingDialog();
dialogService.ShowLoading(dlg);
}
public static Task<string> ShowPromptText()
{
TaskCompletionSource<string> dialogCompletion = new TaskCompletionSource<string>();
PromptDialog dialog = new PromptDialog();
dialog.Canceled += (object sender, object result) => { dialogService.HideDialog(); dialogCompletion.SetResult((string)result); };
dialog.Confirmed += (object sender, object result) => { dialogService.HideDialog(); dialogCompletion.SetResult((string)result); };
dialogService.ShowPrompt(dialog);
return dialogCompletion.Task;
}
public static void HideDialog()
{
dialogService.HideDialog();
}
}
You will notice, that we are using a TaskCompletionSource together with custom event handlers in the ShowPromptText method. This enables us to display the dialog and await the user pressing the okay or cancel button and consuming the returned result.
As of now, the dialogs are as simple as they can get. I use some additional code for styling and themeing, but i will leave that out in order to keep this answer short and simple.
The loading dialog:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Dialogs.LoadingDialog" BackgroundColor="Transparent">
<ContentPage.Content>
<Grid BackgroundColor="#bb000000">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Frame BackgroundColor="Black" CornerRadius="15" Grid.Row="1" x:Name="ContentGrid" Margin="100,0,100,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ActivityIndicator Grid.Row="0" Color="White" IsRunning="True" HorizontalOptions="Center" VerticalOptions="Center"/>
<Label x:Name="LoadingLabel" Text="Loading ..." VerticalOptions="End" HorizontalOptions="Center" Grid.Row="1" TextColor="White" />
</Grid>
</Frame>
</Grid>
</ContentPage.Content>
(no need to post xaml.cs code for this, since there is no interaction with a loading screen)
The prompt dialog
PromptDialog.Xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dialogs="clr-namespace:BetterUI.Dialogs"
x:Class="MyApp.Dialogs.PromptDialog" BackgroundColor="Transparent">
<ContentPage.Content>
<ScrollView>
<Grid BackgroundColor="#bb000000">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Frame x:Name="ContentGrid" Grid.Row="1" CornerRadius="15" BackgroundColor="White" Margin="50,0,50,0" Padding="0">
<Grid Grid.Row="1" Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid x:Name="HeadingGrid" Padding="10, 5, 10, 5" Margin="-15,0,-15,0" Grid.Row="0" BackgroundColor="Black">
<Label x:Name="HeadingLabel" Text="Enter text" TextColor="White" Margin="20,0,20,0"/>
</Grid>
<Label l x:Name="DescriptionLabel" Text="Enter your text" Grid.Row="1" Margin="15,0,15,0"/>
<Entry x:Name="DialogResultText" Placeholder="Text" PlaceholderColor="LightGray" TextColor="Black" Grid.Row="2" Margin="15,0,15,0"/>
<Grid Grid.Row="3" ColumnSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="CancelButton" Text="Cancel" Clicked="OnCancelClick" Grid.Column="0" CornerRadius="0"/>
<Button x:Name="ConfirmButton" Text="Okay" Clicked="OnOkayClick" Grid.Column="1" CornerRadius="0"/>
</Grid>
</Grid>
</Frame>
</Grid>
</ScrollView>
</ContentPage.Content>
PromptDialog.xaml.cs:
public partial class PromptDialog : ContentPage
{
public event EventHandler<object> Confirmed;
public event EventHandler<object> Canceled;
public PromptDialog()
{
InitializeComponent();
}
private void OnCancelClick(object sender, EventArgs e)
{
Canceled?.Invoke(this, string.Empty);
}
private void OnOkayClick(object sender, EventArgs e)
{
Confirmed?.Invoke(this, DialogResultText.Text);
}
}
Android implementation
First of all we will create the android implementation of our IDialogs interface created in the shared code earlier:
[assembly: Dependency(typeof(DialogService))]
namespace MyApp.Droid.Services
{
/// <summary>
/// Handles displaying dialog items on screen
/// </summary>
public class DialogService : IDialogs
{
private static DialogFragment currentDialog;
/// <summary>
/// returns if a dialog is already open
/// </summary>
/// <returns></returns>
public bool IsDialogOpen()
{
return (currentDialog != null && currentDialog.IsVisible);
}
/// <summary>
/// Initialize Dialog Service with activity
/// </summary>
/// <param name="activity">activity</param>
public static void Init(Activity activity)
{
Activity = activity;
}
public static Activity Activity { get; set; }
/// <summary>
/// Displays a loading dialog
/// </summary>
/// <param name="dialog">Instance of progress dialog (xamarin.forms)</param>
public void ShowLoading(Dialogs.LoadingDialog dialog)
{
if (Activity == null)
return;
DialogFragment frag = dialog.CreateDialogFragment(Activity);
frag.SetStyle(DialogFragmentStyle.NoTitle, Resource.Style.DialogFrame);
frag.Show(Activity.FragmentManager, "dialog");
currentDialog = frag;
}
/// <summary>
/// Displays a prompt dialog
/// </summary>
/// <param name="dialog"></param>
public void ShowPrompt(Dialogs.PromptDialog dialog)
{
if (Activity == null)
return;
DialogFragment frag = dialog.CreateDialogFragment(Activity);
frag.SetStyle(DialogFragmentStyle.NoTitle, Resource.Style.DialogFrame);
frag.Show(Activity.FragmentManager, "dialog");
currentDialog = frag;
}
/// <summary>
/// Hides loading dialog
/// </summary>
public void HideDialog()
{
if (Activity == null)
return;
if (currentDialog != null)
{
currentDialog.Dismiss();
currentDialog = null;
}
}
}
}
Note that you have to set the activity for the dialog service prior to calling the actual methods to show a dialog, so in your MainActivity.cs make sure to call
DialogService.Init(this);
after having initialized Xamarin.Forms.
Finally, here comes some black magic:
Usually, one would implement such a dialog in Android by putting a fragment container into the main layout and throwing a fragment inside. Unfortunately, due to using Xamarin.Forms, such a main layout isn't available by default.
Even though Xamarin.Forms offers a view extension, which allows converting a ContentPage to a fragment (ContentPage.CreateFragment), we won't have any success of using this because it needs a target fragment container to be placed in.
However, android provides something called a DialogFragment, which can be thrown at the screen without having the need of a defined fragment container.
Unfortunately there isn't any out-of-the-box solution for creating a DialogFragment from Xamarin Forms. Good news is that this can be overcome with the power of using System.Reflection(;), so we create our own extension method and some modified versions of internal classes, xamarin.forms uses under the hood. In order to achieve this, I took the code from Xamarin.Forms in Xamarin.Platform.Android and modified it to create a DialogFragment from a ContentPage:
public static class PageExtensions
{
public static DialogFragment CreateDialogFragment(this ContentPage view, Context context)
{
if (!Forms.IsInitialized)
throw new InvalidOperationException("call Forms.Init() before this");
// Get Platform constructor via reflection and call it to create new platform object
Platform platform = (Platform)typeof(Platform).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Context), typeof(bool) }, null)
?.Invoke(new object[] { context, true });
// Set the page to the platform
if (platform != null)
{
platform.GetType().GetMethod("SetPage", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(platform, new object[] { view });
// Finally get the view group
ViewGroup vg = (Android.Views.ViewGroup)platform.GetType().GetMethod("GetViewGroup", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(platform, null);
return new EmbeddedDialogFragment(vg, platform);
}
return null;
}
public class DefaultApplication : Xamarin.Forms.Application
{
}
class EmbeddedDialogFragment : DialogFragment
{
readonly ViewGroup _content;
readonly Platform _platform;
bool _disposed;
public EmbeddedDialogFragment()
{
}
public EmbeddedDialogFragment(ViewGroup content, Platform platform)
{
_content = content;
_platform = platform;
}
public override global::Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.Dialog.Window.SetSoftInputMode(SoftInput.AdjustResize);
return _content;
}
public override void OnDestroy()
{
this.Dialog?.Window.SetSoftInputMode(SoftInput.AdjustPan);
base.OnDestroy();
}
protected override void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
_disposed = true;
if (disposing)
{
(_platform as IDisposable)?.Dispose();
}
base.Dispose(disposing);
}
}
}
iOS implementation
Fortunately, for the iOS implementation no deep diving into Xamarin.Forms code is necessary:
Here is the iOS implementation of DialogService:
[assembly: Dependency(typeof(DialogService))]
namespace BetterUI.iOS.Services
{
public class DialogService : IDialogs
{
private UIViewController currentDialog;
private UIWindow popupWindow = null;
public void HideLoading()
{
if (currentDialog != null)
{
UIApplication.SharedApplication.KeyWindow.RootViewController.DismissModalViewController(false);
currentDialog.Dispose();
currentDialog = null;
}
}
public bool IsDialogOpen()
{
return (currentDialog != null && currentDialog.IsBeingPresented);
}
public void ShowLoading(LoadingDialog dialog)
{
UIViewController dialogController = dialog.CreateViewController();
ShowDialog(dialogController);
currentDialog = dialogController;
}
public void ShowPrompt(PromptDialog dialog)
{
UIViewController dialogController = dialog.CreateViewController();
ShowDialog(dialogController);
currentDialog = dialogController;
}
private void ShowDialog(UIViewController dialogController)
{
var bounds = UIScreen.MainScreen.Bounds;
dialogController.View.Frame = bounds;
UIApplication.SharedApplication.KeyWindow.RootViewController.ModalPresentationStyle = UIModalPresentationStyle.CurrentContext;
UIApplication.SharedApplication.KeyWindow.RootViewController.AddChildViewController(dialogController);
UIApplication.SharedApplication.KeyWindow.RootViewController.View.Opaque = false;
UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.AllowsGroupOpacity = true;
UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.BackgroundColor = new CGColor(Color.White.ToCGColor(), 0.0f);
UIApplication.SharedApplication.KeyWindow.RootViewController.View.BackgroundColor = UIColor.Clear;
UIApplication.SharedApplication.KeyWindow.RootViewController.View.AddSubview(dialogController.View);
dialogController.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
dialogController.View.Opaque = false;
dialogController.View.BackgroundColor = UIColor.Clear.ColorWithAlpha(0.0f);
}
}
}
Et voila, now whenever we use the calls from the "Approach"-Section of this post, a nice popup dialog containing our custom Xamarin.Forms ContentPage will show up.
It is easier to use a package, like Plugins.Popup to achieve this, without custom renderers, it is impossible to add an Image to the default AlertDialog, which will limit you.
Using Popup Plugin, you just have to add it to your solution, initialize in both iOS and Android:
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Rg.Plugins.Popup.Popup.Init();
global::Xamarin.Forms.Forms.Init ();
LoadApplication (new App ());
return base.FinishedLaunching (app, options);
}
}
Android:
namespace HelloXamarinFormsWorld.Android
{
[Activity(Label = "HelloXamarinFormsWorld", MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Rg.Plugins.Popup.Popup.Init(this, bundle);
Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication (new App ());
}
}
}
Create a Popup Page
<pages:PopupPage.Animation>
<animations:ScaleAnimation
PositionIn="Center"
PositionOut="Center"
ScaleIn="1.2"
ScaleOut="0.8"
DurationIn="400"
DurationOut="300"
EasingIn="SinOut"
EasingOut="SinIn"
HasBackgroundAnimation="True"/>
</pages:PopupPage.Animation>
<!--You can use any elements here which are extended from Xamarin.Forms.View-->
<StackLayout
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="20, 20, 20, 20">
<Label
Text="Test"/>
</StackLayout>
And, to show in your page:
await Navigation.PushPopupAsync(page);
As of March 22, 2022, there is a Popup Included in the Xamarin Community Toolkit. You can find Microsoft's documentation here.
An object instantiation through XAML:
<xct:Popup xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="clr-namespace:Xamarin.CommunityToolkit.UI.Views;assembly=Xamarin.CommunityToolkit"
x:Class="MyProject.SimplePopup">
<StackLayout>
<Label Text="Hello Simple Popup" />
</StackLayout>
</xct:Popup>
An object instantiation through code:
using Xamarin.CommunityToolkit.UI.Views;
// can also specify a type to be returned from the dismissal of the popup
// e.g. var popup = new Popup<string>
var popup = new Popup
{
// Can set the content to any custom layout
Content = new StackLayout
{
Children =
{
new Label
{
Text = "Hello Simple Popup"
}
}
}
};
You can show the popup through the Navigation stack:
using Xamarin.CommunityToolkit.Extensions;
App.Current.MainPage.Navigation.ShowPopup(popup);
Or call it through await App.Current.MainPage.Navigation.ShowPopupAsync(popup);
It also has events you can intercept and handle, like popup.Dismissed.
The popup can even return a value when it is dismissed if you instantiate it with a type specified:
// provide the <T> type string here for the example
var popup = new Popup<string>
{
Content = new StackLayout
{
Children =
{
new Label
{
Text = "Hello Simple Popup"
},
new Button
{
Text = "Dismiss",
// pass the <T> you would like as the result into Dismiss
// in this case it is a string "Dismiss was clicked"
Command = new Command(() => Dismiss("Dismiss was clicked"))
}
}
}
};
var result = await App.Current.MainPage.Navigation.ShowPopupAsync(popup);
Console.WriteLine(result);
Use package named 'Rg.Plugin.Popup'. It will help you.

UWP virtual keyboard pushes content up

I have an UWP app where my robots face gets pushed up when the virtual keyboard opens. Is it possible to have the scrollviewer stay in place and have the textbox stay in view when the virtual keyboard opens.
I see you can subscribe to the opening and hiding events but that does not give me any options on which ui elements can stay or hide. https://learn.microsoft.com/en-us/windows/uwp/input-and-devices/respond-to-the-presence-of-the-touch-keyboard
<Page
x:Class="VirtualKeyboardFix.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:VirtualKeyboardFix"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer >
<Image Source="image.png" />
</ScrollViewer>
<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="10" />
</Grid>
</Page>
Yes that is the answer Justin XL. Thanks a lot. If you want to post it as a reply instead of a comment then I will mark it as the answer.
Just in case anyone has the same problem.
namespace VirtualKeyboardFix
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
InputPane.GetForCurrentView().Showing += MainPage_Showing;
InputPane.GetForCurrentView().Hiding += MainPage_Hiding;
}
private void MainPage_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
args.EnsuredFocusedElementInView = false;
InputTextBox.Margin = new Thickness(10, 10, 10, args.OccludedRect.Height + 10);
}
private void MainPage_Hiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
args.EnsuredFocusedElementInView = false;
InputTextBox.Margin = new Thickness(10, 10, 10, 10);
}
}
}

GridViewItem from GridView

In UWP (or WinRt) how can I get in code behind a GridViewItem from a GridView?
I tried
this.AppsGridView.ContainerFromItem(this.AppsGridView.Items[0]);
and it returns null. What else can I try? Also, is there a simple way to enumerate through all the GridViewItem object in a GridView?
I tested this, and it should works.
Code:
MainPage.xaml
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<GridView x:Name="GridView">
<GridView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" Background="Blue"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Button Background="Red" Content="GetItems" Click="OnGetItemsClick"/>
MainPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
GridView.ItemsSource = new List<int>() {1, 2, 3, 4, 5, 6, 7};
base.OnNavigatedTo(e);
}
private void OnGetItemsClick(object sender, RoutedEventArgs e)
{
var item = GridView.Items[0];
var itemContainer = GridView.ContainerFromItem(item);
}
And this code works well when you define items manually :)

XAML c# get window location

How do programmers for XAML C# find the current position of a wpf application's window? there is a function to find the startup location but it's crucial that when the window is moved by dragging it to another side the position changes, so i always need the current position so that another function acts accordingly relative to the wpf window position.
I am not sure about how to find window position but using cursor I have done it to find the current location, may be that can guide you. for knowing the current position of cursor you should be inside the window else it will always sho 0,0. Code is like this:
Xaml File:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Application 2" Height="350" Width="325"
MouseDown="Window_MouseDown"
AllowDrop="True" DragOver="Window_DragOver">
<StackPanel>
<Label Name="lblInfo1" Content="Info 1"/>
<Label Name="lblInfo2" Content="Info 2"/>
</StackPanel>
</Window>
C# file:
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
DragDrop.DoDragDrop((DependencyObject)e.Source, "Sample", DragDropEffects.Copy);
}
private void Window_DragOver(object sender, DragEventArgs e)
{
System.Windows.Point p1 = Mouse.GetPosition(this);
lblInfo1.Content = string.Format("Mouse.GetPosition: {0}, {1}", p1.X, p1.Y);
System.Windows.Point p2 = e.GetPosition(this);
lblInfo2.Content = string.Format("DragEventArgs.GetPosition: {0}, {1}", p2.X, p2.Y);
}
Try it.
For code-behind binding button with event:
private void b_Click(object sender, EventArgs e)
{
// your code...
}
Xaml file:
<Button Click="ButtonClicked">Button</Button>

GoToState does not work for ControlTemplate in UserControl

I am totally lost and I would really appreciate your help on this.
My final goal is to create a user control that will contain two control templates. Square and a Circle. Based on a type the control will display one or the other. When the mouse enters the shape the Opacity will change to 0.2.
The first part works but the Opacity does not change. The event is triggered and a GoToState is called, but with no result. The Opacity stays 1.
My XAML:
<UserControl.Resources>
<ControlTemplate x:Key="TemplateSquare" TargetType="{x:Type local:KeyControl}">
<Canvas x:Name="MainCanvas" VerticalAlignment="Center" HorizontalAlignment="Center">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="CenterRectangle" Storyboard.TargetProperty="(UIElement.Opacity)" Duration="0" To=".2"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="CenterRectangle" Fill="Red" Width="100" Height="100"></Rectangle>
</Canvas>
</ControlTemplate>
</UserControl.Resources>
<!-- IF I MOVE THE CANVAS HERE THE OPACITY CHANGES ON MOUSE OVER -->
Codebehind:
public partial class KeyControl : UserControl
{
private bool _isPressed = false;
private bool _isMouseOver = false;
public KeyControl()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(KeyControl_Loaded);
}
private void KeyControl_Loaded(object sender, RoutedEventArgs e)
{
//this will be set in the Type setter
this.Template = this.FindResource("TemplateSquare") as ControlTemplate;
this.MouseEnter += new MouseEventHandler(CorePart_MouseEnter);
this.MouseLeave += new MouseEventHandler(CorePart_MouseLeave);
GoToState(false);
}
private void GoToState(bool useTransitions)
{
if (_isPressed)
VisualStateManager.GoToState(this, "Pressed", useTransitions);
else if (_isMouseOver)
VisualStateManager.GoToState(this, "MouseOver", useTransitions);
else
VisualStateManager.GoToState(this, "Normal", useTransitions);
}
private void CorePart_MouseLeave(object sender, MouseEventArgs e)
{
_isMouseOver = false;
GoToState(true);
}
private void CorePart_MouseEnter(object sender, MouseEventArgs e)
{
_isMouseOver = true;
GoToState(true);
}
}
Can somebody please tell me where the problem could be?
Thank You
The UserControl makes it's Content the "root" element, which is used to locate the VisualStateGroups. If you use Reflector and look at UserControl.StateGroupsRoot, you'd see it looks like:
internal override FrameworkElement StateGroupsRoot {
get {
return (base.Content as FrameworkElement);
}
}
While FrameworkElement (and thus most other elements) use:
internal virtual FrameworkElement StateGroupsRoot {
get {
return (this._templateChild as FrameworkElement);
}
}
When setting the Template property, the Content property will still be null. When you move the Canvas to be below Resources, you are setting the Content property. So the visual state groups can be found in that case.
You can work around this by changing your control derive from ContentControl directly, and bypass UserControl. Simply change UserControl references to ContentControl in your XAML and code-behind.

Resources