dotnet / dotnet/wpf

Controlling UIElement refresh rate

Open
#9,326 1 comment 1 reaction 0 assignees View on GitHub
Investigate
Dominant language
C#
Stars
7.7k
Forks
1.3k
Avg merge
1d 11h
Merged PRs (30d)
61

Description

I noticed via the System.Windows.Media.CompositionTarget.Rendering event that the app's frames per second are very irregular, when I use the mouse to interact with a visual control, the frames per second instantly goes from around 60 to over 400, and it occurred to me that this is most likely due to my customizing the style of the control.
So I wondered if I could limit the frames per second of a UIElement through code; I found this code, but it seems to only limit the framerate of the final rendering of the entire app, and it still doesn't limit the frames per second of a UIElement
``` csharp
private void Application_Startup(object sender, StartupEventArgs e)
{
Timeline.DesiredFrameRateProperty.OverrideMetadata(
typeof(Timeline), new FrameworkPropertyMetadata(
new FrameworkPropertyMetadata { DefaultValue = 60 }
);
}
```
Here's the frame rate monitor I mentioned above along with the custom control styling code:
``` csharp
ulong __frameCounter = 0;
readonly Stopwatch __stopwatch = new();
private void CompositionTarget_Rendering(object? sender, EventArgs e)
{
if (__frameCounter++ == 0)
{
// Starting timing.
__stopwatch.Start();
}

// Determine frame rate in fps (frames per second).
if (__frameCounter >= 60)
{
long frameRate = (long)(__frameCounter / __stopwatch.Elapsed.TotalSeconds);
DebugLabel_FPS.Content = $"FPS: {frameRate}";
__frameCounter = 0;
__stopwatch.Restart();
}
}
```
``` xaml

<Setter Property="Background" Value="{DynamicResource Palette_500}"/>
<Setter Property="Foreground" Value="{DynamicResource Palette_50}"/>
<Setter Property="BorderBrush" Value="{x:Null}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="15.6"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Effect="{StaticResource EffectShadow3}" Background="{TemplateBinding Background}" CornerRadius="{DynamicResource CornerRadiusCornerRadius}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Effect="{StaticResource EffectShadow3}" Background="{TemplateBinding Background}" CornerRadius="{DynamicResource CornerRadiusCornerRadius}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>

```
``` csharp
public static class UIAnimation
{
public static void Refresh()
{
MouseDownColorAnimation = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_500"], Duration = TimeSpan.FromMilliseconds(100) };
MouseUpColorAnimation = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_300"], Duration = TimeSpan.FromMilliseconds(300) };
MouseEnterColorAnimation = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_300"], Duration = TimeSpan.FromMilliseconds(120) };
MouseLeaveColorAnimation = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_500"], Duration = TimeSpan.FromMilliseconds(170) };
}

public static ColorAnimation MouseDownColorAnimation { get; private set; } = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_500"], Duration = TimeSpan.FromMilliseconds(100) };
public static ColorAnimation MouseUpColorAnimation { get; private set; } = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_300"], Duration = TimeSpan.FromMilliseconds(300) };
public static ColorAnimation MouseEnterColorAnimation { get; private set; } = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_200"], Duration = TimeSpan.FromMilliseconds(120) };
public static ColorAnimation MouseLeaveColorAnimation { get; private set; } = new() { To = (Color)((ResourceDictionary)App.Current.Resources["CurrentColorPalette"])["Palette_300"], Duration = TimeSpan.FromMilliseconds(170) };

public static DoubleAnimation SmallerScaleAnimation { get; private set; } = new() { To = 0.97, Duration = TimeSpan.FromSeconds(0.15), EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut } };
public static DoubleAnimation SmallSmallerScaleAnimation { get; private set; } = new() { To = 0.92, Duration = TimeSpan.FromSeconds(0.15), EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut } };
public static DoubleAnimation NormalScaleAnimation { get; private set; } = new() { To = 1, Duration = TimeSpan.FromSeconds(0.25), EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut } };
public static DoubleAnimation BiggerScaleAnimation { get; private set; } = new() { To = 1.03, Duration = TimeSpan.FromSeconds(0.15), EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut } };

public static void Button_MouseUp(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

((Control)sender).Background.BeginAnimation(SolidColorBrush.ColorProperty, MouseUpColorAnimation);

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];

st.BeginAnimation(ScaleTransform.ScaleXProperty, NormalScaleAnimation);
st.BeginAnimation(ScaleTransform.ScaleYProperty, NormalScaleAnimation);

var ue = (UIElement)sender;
var uers = ue.RenderSize;
if (e != null)
{
var rpos = e.GetPosition(ue);
var rpx = rpos.X; var rpy = rpos.Y;
if (!(
(rpx > uers.Width || rpy > uers.Height) ||
(rpx < 0 || rpy < 0)
))
{ Button_MouseEnter(sender, e); }
}
}
public static void Button_MouseDown(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

((Control)sender).Background.BeginAnimation(SolidColorBrush.ColorProperty, MouseDownColorAnimation);

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];

if (e != null)
{
var p = e.MouseDevice.GetPosition((Control)sender);

st.CenterX = p.X;
st.CenterY = p.Y;
}

st.BeginAnimation(ScaleTransform.ScaleXProperty, SmallSmallerScaleAnimation);
st.BeginAnimation(ScaleTransform.ScaleYProperty, SmallSmallerScaleAnimation);
}
public static void Button_MouseLeave(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

((Control)sender).Background.BeginAnimation(SolidColorBrush.ColorProperty, MouseLeaveColorAnimation);
}
public static void Button_MouseEnter(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

((Control)sender).Background.BeginAnimation(SolidColorBrush.ColorProperty, MouseEnterColorAnimation);
}
public static void Button_MouseMove(object sender, MouseEventArgs? e)
{
if (e != null)
{
var p = e.MouseDevice.GetPosition((Control)sender);

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];
// Inverted
st.CenterX = p.X;
st.CenterY = p.Y;
}
}
public static void InitButton(Control button)
{
try
{
button.Resources["UIA_Locked"] = false;
button.RenderTransform = new TransformGroup { Children = new TransformCollection(new Transform[] { new ScaleTransform(1, 1, 1, 1) }) };
var color = (SolidColorBrush)button.Background == null ? Colors.Transparent : ((SolidColorBrush)button.Background).Color;
button.Background = new SolidColorBrush(color);

button.MouseEnter -= Button_MouseEnter;
button.MouseLeave -= Button_MouseLeave;
button.PreviewMouseDown -= Button_MouseDown;
button.PreviewMouseUp -= Button_MouseUp;
button.PreviewMouseMove -= Button_MouseMove;

button.MouseEnter += Button_MouseEnter;
button.MouseLeave += Button_MouseLeave;
button.PreviewMouseDown += Button_MouseDown;
button.PreviewMouseUp += Button_MouseUp;
button.PreviewMouseMove += Button_MouseMove;

Button_MouseLeave(button, null);
}
catch (Exception)
{
InitCard(button);
}
}
public static void InitButtons(IEnumerable buttons)
{
foreach (var button in buttons)
{
InitButton(button);
}
}

public static void Card_MouseUp(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];

st.BeginAnimation(ScaleTransform.ScaleXProperty, NormalScaleAnimation);
st.BeginAnimation(ScaleTransform.ScaleYProperty, NormalScaleAnimation);
}
public static void Card_MouseDown(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];

if (e != null)
{
var p = e.MouseDevice.GetPosition((Control)sender);

st.CenterX = p.X;
st.CenterY = p.Y;
}

st.BeginAnimation(ScaleTransform.ScaleXProperty, SmallerScaleAnimation);
st.BeginAnimation(ScaleTransform.ScaleYProperty, SmallerScaleAnimation);
}
public static void Card_MouseLeave(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];

st.BeginAnimation(ScaleTransform.ScaleXProperty, NormalScaleAnimation);
st.BeginAnimation(ScaleTransform.ScaleYProperty, NormalScaleAnimation);

//var anim = MouseLeaveColorAnimation;
//anim.To = (Color)((Control)sender).Resources["_UIA_Color"];
//((Control)sender).Background.BeginAnimation(SolidColorBrush.ColorProperty, anim);
}
public static void Card_MouseEnter(object sender, MouseEventArgs? e)
{
if ((bool)((Control)sender).Resources["UIA_Locked"])
return;

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];

if (e != null)
{
var p = e.MouseDevice.GetPosition((Control)sender);

st.CenterX = p.X;
st.CenterY = p.Y;
}

st.BeginAnimation(ScaleTransform.ScaleXProperty, BiggerScaleAnimation);
st.BeginAnimation(ScaleTransform.ScaleYProperty, BiggerScaleAnimation);
//((Control)sender).Background.BeginAnimation(SolidColorBrush.ColorProperty, MouseEnterColorAnimation);
}
public static void Card_MouseMove(object sender, MouseEventArgs? e)
{
if (e != null)
{
var p = e.MouseDevice.GetPosition((Control)sender);

TransformGroup TG = (TransformGroup)((Control)sender).RenderTransform;
ScaleTransform st = (ScaleTransform)TG.Children[0];
st.CenterX = p.X;
st.CenterY = p.Y;
}
}
public static void InitCard(Control card)
{
try
{
//InitButton(card);
card.Resources["UIA_Locked"] = false;
card.RenderTransform = new TransformGroup { Children = new TransformCollection(new Transform[] { new ScaleTransform(1, 1, .5, .5) }) };
//var color = (SolidColorBrush)card.Background == null ? Colors.Transparent : ((SolidColorBrush)card.Background).Color;
//card.Background = new SolidColorBrush(color);

//card.Resources["_UIA_Color"] = color;
card.MouseEnter -= Card_MouseEnter;
card.MouseLeave -= Card_MouseLeave;
card.PreviewMouseDown -= Card_MouseDown;
card.PreviewMouseUp -= Card_MouseUp;
card.PreviewMouseMove -= Card_MouseMove;

card.MouseEnter += Card_MouseEnter;
card.MouseLeave += Card_MouseLeave;
card.PreviewMouseDown += Card_MouseDown;
card.PreviewMouseUp += Card_MouseUp;
card.PreviewMouseMove += Card_MouseMove;

Card_MouseLeave(card, null);
}
catch (Exception)
{ }
}
public static void InitCards(IEnumerable cards)
{
foreach (var card in cards)
{
InitCard(card);
}
}
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.