I want to implement a IValueConverter class, which converts text from textBox to TimeSpan format string. And further if user inputs simple number, it should be converted to TimeSpan, eg. 65.8 -> 0:01:05.800
The converter works fine, it is called after each keystroke, thus if I want to write 65.8, it is converted to 0:00:06 immediately.
What is wrong?
IValueConverter
Note that Solver is static class, where the time format is checked etc., this works properly
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string time = (string)value;
TimeSpan _ts = new TimeSpan();
if (Solver.OnlySeconds(time, out _ts))
{
return _ts.ToString(Solver.__TFORMAT);
}
else return time;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (string)value;
}
XAML
<TextBox x:Name="txtTime" Width="100" Height="25" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding RelativeSource={RelativeSource self},Path=Text, Converter={StaticResource ConvertToTime}, UpdateSourceTrigger=LostFocus}"/>
Solver
public class Solver
{
#region constants
/// <summary>
/// Output TimeSpan format
/// </summary>
public const string __TFORMAT = #"%h\:mm\:ss\.fff";
/// <summary>
/// hours and minutes separator
/// </summary>
public const string __COL = ":";
/// <summary>
/// Negative sign
/// </summary>
public const string __NEG = "-";
private const int __PREC = 1000; //determines precision to 1/1000 of second
private const long __SECTOTICK = 10000000;
#endregion
/// <summary>
/// Determines if value is convertable to seconds and creates new TimeSpan
/// </summary>
/// <param name="time">Time</param>
/// <param name="TS">out, timeSpan representing Time value</param>
/// <returns>True if succesfull, false if not</returns>
#region public Methods
public static bool OnlySeconds(string time, out TimeSpan TS)
{
double dSeconds;
if (Double.TryParse(time, out dSeconds))
{
long ticks = (long)(dSeconds * __SECTOTICK);
TS = new TimeSpan(ticks);
return true;
}
TS = new TimeSpan();
return false;
}
/// <summary>
/// Determines if value is valid TimeSpan Format
/// </summary>
/// <param name="time">Time</param>
/// <param name="TS">out TimeSpan value alwas with 0 days</param>
/// <returns>true if conversions succesfull</returns>
public static bool IsTimeValue(string time, out TimeSpan TS)
{
TimeSpan _ts = new TimeSpan();
if (OnlySeconds(time,out TS))
{
return true;
}
if (TimeSpan.TryParse(time,out _ts))
{
TS = determineTimeFromString(time);
return true;
}
return false;
}
#endregion
#region private methods
/// <summary>
/// Converts selected string to TImeSpan. String has to be in valid TimeSpan format. Erases days
/// </summary>
/// <param name="sTime">Time</param>
/// <returns>Time span</returns>
private static TimeSpan determineTimeFromString(string sTime)
{
int _iColon = Regex.Matches(sTime, __COL).Count;
string _sResult;
TimeSpan _tsDays = new TimeSpan();
if (TimeSpan.TryParse(sTime, out _tsDays))
{
if (_tsDays.Days > 0)
return new TimeSpan(0, _tsDays.Hours, _tsDays.Minutes, _tsDays.Seconds, _tsDays.Milliseconds);
}
TimeSpan _ts = new TimeSpan();
if (_iColon == 1) //minutes and seconds
{
//add 0 hours and 0 days
_sResult = addTimeToTimeSpan(sTime, "0.0:");
}
else if
(_iColon == 2) //hours minutes and seconds
{
//add 0 days
_sResult = addTimeToTimeSpan(sTime, "0.");
}
else _sResult = sTime;
if (TimeSpan.TryParse(_sResult, out _ts))
{
// in all cases remove day
return new TimeSpan(0, _ts.Hours, _ts.Minutes, _ts.Seconds, _ts.Milliseconds);
}
else return _ts;
}
/// <summary>
/// Adds time days or hours to time string
/// </summary>
/// <param name="sTime">string original time</param>
/// <param name="sTimeToAdd">string to ADD</param>
/// <returns>string</returns>
private static string addTimeToTimeSpan(string sTime, string sTimeToAdd)
{
string _sResult;
if (sTime.StartsWith(__NEG))
{
_sResult = __NEG + sTimeToAdd + sTime.Remove(0, 1);
}
else _sResult = sTimeToAdd + sTime;
return _sResult;
}
#endregion region
}
}
You should create an Attached Behavior to convert TextBox.Text on TextBox.LostFocus:
TextBox.cs
public class TextBox : DependencyObject
{
#region IsTextConversionEnabled attached property
public static readonly DependencyProperty IsTextConversionEnabledProperty = DependencyProperty.RegisterAttached(
"IsTextConversionEnabled",
typeof(bool),
typeof(TextBox),
new PropertyMetadata(false, TextBox.OnIsTextConversionEnabledChanged));
public static void SetIsTextConversionEnabled([NotNull] DependencyObject attachingElement, bool value) => attachingElement.SetValue(TextBox.IsTextConversionEnabledProperty, value);
public static bool GetIsTextConversionEnabled([NotNull] DependencyObject attachingElement) => (bool) attachingElement.GetValue(TextBox.IsTextConversionEnabledProperty);
#endregion
#region Converter attached property
public static readonly DependencyProperty ConverterProperty = DependencyProperty.RegisterAttached(
"Converter",
typeof(IValueConverter),
typeof(TextBox),
new PropertyMetadata(default(IValueConverter)));
public static void SetConverter([NotNull] DependencyObject attachingElement, IValueConverter value) => attachingElement.SetValue(TextBox.ConverterProperty, value);
public static IValueConverter GetConverter([NotNull] DependencyObject attachingElement) => (IValueConverter) attachingElement.GetValue(TextBox.ConverterProperty);
#endregion
private static void OnIsTextConversionEnabledChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
{
if (!(attachingElement is System.Windows.Controls.TextBox textBox))
{
return;
}
bool isEnabled = (bool) e.NewValue;
if (isEnabled)
{
textBox.LostFocus += TextBox.ConvertTextOnLostFocus;
}
else
{
textBox.LostFocus -= TextBox.ConvertTextOnLostFocus;
}
}
private static void ConvertTextOnLostFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as System.Windows.Controls.TextBox;
textBox.Text = TextBox.GetConverter(textBox)?.Convert(
textBox.Text,
typeof(string),
string.Empty,
CultureInfo.CurrentUICulture) as string ?? textBox.Text;
}
}
Usage
<Resources>
<!-- Your IValueConverter implementation -->
<DoubleToTimeStampConverter x:Key="DoubleToTimeStampConverter" />
</Resources>
<TextBox TextBox.IsTextConversionEnabled="True"
TextBox.Converter="{StaticResource DoubleToTimeStampConverter}" />
An alternative solution would be to extend TextBox and handle the LostFocus event internally.
Related
I have a media player in which I want to hide the aspect ratio, cast to device and full screen icon in media player. This is the screenshot of my media player in which I have marked the icon which I need to hide in red color.
So I have taken this media player from this link. This is the my Xaml code
<StackLayout>
<Button Text="Stretch" Clicked="Button_Clicked"/>
<forms1:MediaElement HorizontalOptions="Fill" BackgroundColor="Green" VerticalOptions="Center" HeightRequest="180" x:Name="Media" IsLooping="True" AreTransportControlsEnabled="true" Source="http://video.ch9.ms/ch9/334f/891b78a5-642d-40b4-8d02-ff40ffdd334f/LoginToLinkedinUSingXamarinAuth_mid.mp4"/>
</StackLayout>
This is my media element code-
public sealed class MediaElement : View
{
/// <summary>
/// Identifies the AreTransportControlsEnabled dependency property.
/// </summary>
public static readonly BindableProperty AreTransportControlsEnabledProperty =
BindableProperty.Create(nameof(AreTransportControlsEnabled), typeof(bool), typeof(MediaElement), false);
/// <summary>
/// Identifies the AutoPlay dependency property.
/// </summary>
public static readonly BindableProperty AutoPlayProperty =
BindableProperty.Create(nameof(AutoPlay), typeof(bool), typeof(MediaElement), true);
/// <summary>
/// Identifies the BufferingProgress dependency property.
/// </summary>
public static readonly BindableProperty BufferingProgressProperty =
BindableProperty.Create(nameof(BufferingProgress), typeof(double), typeof(MediaElement), 0.0);
/// <summary>
/// Identifies the IsLooping dependency property.
/// </summary>
public static readonly BindableProperty IsLoopingProperty =
BindableProperty.Create(nameof(IsLooping), typeof(bool), typeof(MediaElement), false);
/// <summary>
/// Identifies the KeepScreenOn dependency property.
/// </summary>
public static readonly BindableProperty KeepScreenOnProperty =
BindableProperty.Create(nameof(KeepScreenOn), typeof(bool), typeof(MediaElement), false);
/// <summary>
/// Identifies the Source dependency property.
/// </summary>
public static readonly BindableProperty SourceProperty =
BindableProperty.Create(nameof(Source), typeof(Uri), typeof(MediaElement));
/// <summary>
/// Identifies the CurrentState dependency property.
/// </summary>
public static readonly BindableProperty CurrentStateProperty =
BindableProperty.Create(nameof(CurrentState), typeof(MediaElementState), typeof(MediaElement), MediaElementState.Closed);
/// <summary>
/// Identifies the Position dependency property.
/// </summary>
public static readonly BindableProperty PositionProperty =
BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(MediaElement), TimeSpan.Zero, validateValue: ValidatePosition);
private static bool ValidatePosition(BindableObject bindable, object value)
{
MediaElement element = bindable as MediaElement;
if (element != null)
{
if (element._renderer != null)
{
element._renderer.Seek((TimeSpan)value);
}
}
return true;
}
/// <summary>
/// Identifies the Stretch dependency property.
/// </summary>
public static readonly BindableProperty StretchProperty =
BindableProperty.Create(nameof(Stretch), typeof(Stretch), typeof(MediaElement), Stretch.Uniform);
private IMediaElementRenderer _renderer = null;
public void SetRenderer(IMediaElementRenderer renderer)
{
_renderer = renderer;
}
/// <summary>
/// Gets or sets a value that determines whether the standard transport controls are enabled.
/// </summary>
public bool AreTransportControlsEnabled
{
get { return (bool)GetValue(AreTransportControlsEnabledProperty); }
set { SetValue(AreTransportControlsEnabledProperty, value); }
}
/// <summary>
/// Gets or sets a value that indicates whether media will begin playback automatically when the <see cref="Source"/> property is set.
/// </summary>
public bool AutoPlay
{
get { return (bool)GetValue(AutoPlayProperty); }
set { SetValue(AutoPlayProperty, value); }
}
/// <summary>
/// Gets a value that indicates the current buffering progress.
/// </summary>
/// <value>The amount of buffering that is completed for media content.
/// The value ranges from 0 to 1.
/// Multiply by 100 to obtain a percentage.</value>
public double BufferingProgress
{
get
{
return (double)GetValue(BufferingProgressProperty);
}
}
/// <summary>
/// Gets or sets a value that describes whether the media source currently loaded in the media engine should automatically set the position to the media start after reaching its end.
/// </summary>
public bool IsLooping
{
get { return (bool)GetValue(IsLoopingProperty); }
set { SetValue(IsLoopingProperty, value); }
}
/// <summary>
/// Gets or sets a value that specifies whether the control should stop the screen from timing out when playing media.
/// </summary>
public bool KeepScreenOn
{
get { return (bool)GetValue(KeepScreenOnProperty); }
set { SetValue(KeepScreenOnProperty, value); }
}
public TimeSpan NaturalDuration
{
get
{
if (_renderer != null)
{
return _renderer.NaturalDuration;
}
return TimeSpan.Zero;
}
}
public int NaturalVideoHeight
{
get
{
if (_renderer != null)
{
return _renderer.NaturalVideoHeight;
}
return 0;
}
}
public int NaturalVideoWidth
{
get
{
if (_renderer != null)
{
return _renderer.NaturalVideoWidth;
}
return 0;
}
}
/// <summary>
/// Gets or sets a media source on the MediaElement.
/// </summary>
[TypeConverter(typeof(Xamarin.Forms.UriTypeConverter))]
public Uri Source
{
get { return (Uri)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
private IDictionary<string, string> _httpHeaders = new Dictionary<string, string>();
public IDictionary<string, string> HttpHeaders
{
get
{
return _httpHeaders;
}
}
/// <summary>
/// Gets the status of this MediaElement.
/// </summary>
public MediaElementState CurrentState
{
get { return (MediaElementState)GetValue(CurrentStateProperty); }
internal set
{
SetValue(CurrentStateProperty, value);
}
}
public void RaiseCurrentStateChanged()
{
CurrentStateChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Gets or sets the current position of progress through the media's playback time.
/// </summary>
public System.TimeSpan Position
{
get
{
if (_renderer != null)
{
return _renderer.Position;
}
return (TimeSpan)GetValue(PositionProperty);
}
set
{
SetValue(PositionProperty, value);
}
}
/// <summary>
/// Plays media from the current position.
/// </summary>
public void Play()
{
CurrentState = MediaElementState.Playing;
}
/// <summary>
/// Pauses media at the current position.
/// </summary>
public void Pause()
{
if (CurrentState == MediaElementState.Playing)
{
CurrentState = MediaElementState.Paused;
}
}
/// <summary>
/// Stops and resets media to be played from the beginning.
/// </summary>
public void Stop()
{
if (CurrentState != MediaElementState.Stopped)
{
CurrentState = MediaElementState.Stopped;
}
}
/// <summary>
/// Gets or sets a value that describes how an MediaElement should be stretched to fill the destination rectangle.
/// </summary>
/// <value>A value of the <see cref="Stretch"/> enumeration that specifies how the source visual media is rendered.
/// The default value is Uniform.</value>
public Stretch Stretch
{
get
{
return (Stretch)GetValue(StretchProperty);
}
set
{
SetValue(StretchProperty, value);
}
}
/// <summary>
/// Occurs when the value of the <see cref="CurrentState"/> property changes.
/// </summary>
public event EventHandler CurrentStateChanged;
/// <summary>
/// Occurs when the MediaElement finishes playing audio or video.
/// </summary>
public event EventHandler MediaEnded;
public void RaiseMediaOpened()
{
MediaOpened?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Occurs when the media stream has been validated and opened, and the file headers have been read.
/// </summary>
public event EventHandler MediaOpened;
public void RaiseSeekCompleted()
{
SeekCompleted?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Occurs when the seek point of a requested seek operation is ready for playback.
/// </summary>
public event EventHandler SeekCompleted;
public void OnMediaEnded()
{
CurrentState = MediaElementState.Stopped;
if (MediaEnded != null)
{
System.Diagnostics.Debug.WriteLine("Media Ended");
MediaEnded(this, EventArgs.Empty);
}
}
internal void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
}
public interface IMediaElementRenderer
{
double BufferingProgress { get; }
TimeSpan NaturalDuration { get; }
int NaturalVideoHeight { get; }
int NaturalVideoWidth { get; }
TimeSpan Position { get; }
void Seek(TimeSpan time);
}
}
I don't have any clue how hide those icons. Any suggestions?
The basic animations in UWP is good and all but I would like to create my own animation. So I looked at the different animations and saw that they all are a subclass of Timeline. Just to test, I decided to do a copy of the DoubleAnimation class like this:
public sealed class MyAnimation : Timeline
{
public static DependencyProperty _ByProperty;
public static DependencyProperty _EasingFunctionProperty;
public static DependencyProperty _EnableDependentAnimationProperty;
public static DependencyProperty _FromProperty;
public static DependencyProperty _ToProperty;
public MyAnimation() : base()
{
}
static MyAnimation()
{
_ByProperty = DependencyProperty.Register("By", typeof(double?), typeof(MyAnimation), new PropertyMetadata((double?)null));
_EasingFunctionProperty = DependencyProperty.Register("EasingFunction", typeof(EasingFunctionBase), typeof(MyAnimation), new PropertyMetadata(null));
_EnableDependentAnimationProperty = DependencyProperty.Register("EnableDependentAnimation", typeof(bool), typeof(MyAnimation), new PropertyMetadata(false));
_FromProperty = DependencyProperty.Register("From", typeof(double?), typeof(MyAnimation), new PropertyMetadata((double?)null));
_ToProperty = DependencyProperty.Register("To", typeof(double?), typeof(MyAnimation), new PropertyMetadata((double?)null));
}
public static DependencyProperty ByProperty { get { return _ByProperty; } }
public static DependencyProperty EasingFunctionProperty { get { return _EasingFunctionProperty; } }
public static DependencyProperty EnableDependentAnimationProperty { get { return _EnableDependentAnimationProperty; } }
public static DependencyProperty FromProperty { get { return _FromProperty; } }
public static DependencyProperty ToProperty { get { return _ToProperty; } }
public double? To { get { return (double?)GetValue(_ToProperty); } set { SetValue(_ToProperty, value); } }
public double? From { get { return (double?)GetValue(_FromProperty); } set { SetValue(_FromProperty, value); } }
public bool EnableDependentAnimation { get { return (bool)GetValue(_EnableDependentAnimationProperty); } set { SetValue(_EnableDependentAnimationProperty, value); } }
public EasingFunctionBase EasingFunction { get { return (EasingFunctionBase)GetValue(_EasingFunctionProperty); } set { SetValue(_EasingFunctionProperty, value); } }
public double? By { get { return (double?)GetValue(_ByProperty); } set { SetValue(_ByProperty, value); } }
}
Then I created an object to move:
<Grid Name="Root" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Ellipse Width="200" Height="200" Fill="Green" Name="MyEllipse"/>
</Grid>
And then I start the animation when everything is loaded:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Storyboard sb = new Storyboard();
MyAnimation da = new MyAnimation();
da.From = 200;
da.To = 500;
da.Duration = new Duration(TimeSpan.FromSeconds(5));
da.EnableDependentAnimation = true;
sb.Children.Add(da);
Storyboard.SetTargetProperty(da, "Width");
Storyboard.SetTarget(da, MyEllipse);
sb.Begin();
}
Now to my problems. When I run this I get the following exception:
ERROR: System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component.
at Windows.UI.Xaml.Media.Animation.Storyboard.Begin()
at TestAnimation.MainPage.Button_Click(Object sender, RoutedEventArgs e)
Which gives me zero explanation for what went wrong. I am guessing something more needs to be done inside the constructor but I can not read the source code of the DoubleAnimation, only the metadata, which makes it impossible to know what actually happens. Anyone know what needs to be done in order to get this to work?
Please read the comments on the question because I think they still give a better answer to the question
But, I came across this question as I was also getting a
ERROR: System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component
In my case it had nothing to do with the animation system. It was because I had made a mistake in my scaling algorithm and was returning a desiredSize of new Size(27688, 8) from my override MeasureOverride(...).
From I read and after multiple attempts, UWP does not offer the means to create your own animations as there are no methods to override from Timeline.
My scenario was supposed to be a simple one - how do I animate a Grid using a DoubleAnimation?! Sounds trivial, but a) Grid takes GridLength and not Double, b) one cannot attach an IValueConverter to StoryBoard.TargetProperty, and c) creating your own custom animation seems not to be supported in UWP.
As such, the only option remaining is to leverage from the existing animations and build a layer on top. The most common approach is to resort to two Dependency Properties in the Page, bind one with the DoubleAnimation and another with the Grid. Works, but it is not scalable, especially if you want to decouple your styling.
Here is the outline of my approach:
- Create a global ConverterService that attaches a property "Converter" to any Dependency Object
- Create a generic converter that is an IValueConverter
- The generic value converter has a DependencyProperty called Input of type Tin, and another DependencyProperty called Output of type Tout
- Bind the DoubleAnimation with the Input of your converter and the Output with the Grid.
Here is how it is used:
<Grid Style="{StaticResource MainGrid}" x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=MyMove, Path=Output}" x:Name="MyColumn">
<conv:ConverterService.Converter>
<conv:DoubleToGridLengthConverter x:Name="MyMove" Input="2" GridUnit="Star" />
</conv:ConverterService.Converter>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<stateTriggers:WindowAspectRatioTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Storyboard>
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0"
EnableDependentAnimation="True"
Storyboard.TargetName ="MyMove"
Storyboard.TargetProperty="Input"
From="1" To="15" Duration="0:0:3" FillBehavior="HoldEnd" />
</Storyboard>
</VisualState.Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
And of course, mind to import the namespaces at the top of your page.
In my case:
<base:BasePage
x:Class="MyProject.MainPage"
x:Name="MyPage"
xmlns:base ="using:MyProject.Base"
xmlns:local="using:MyProject"
xmlns:conv="using:MyProject.Converters"
xmlns:stateTriggers="using:MyProject.StateTriggers"
xmlns:ctrl="using:MyProject.UserControls"
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"
mc:Ignorable="d">
And here is the code for the ConverterService and BaseConverter and DoubleToGridLengthConverter:
/// <summary>A generic converter.</summary>
public interface IConverter: IValueConverter
{
/// <summary>The input value.</summary>
object Input { get; set; }
/// <summary>The output value.</summary>
object Output { get; set; }
}
/// <summary>A service that provides conversion capabilities to dependency objects via an attached property.</summary>
public abstract class ConverterService : DependencyObject
{
/// <summary>The Converter dependency property.</summary>
public static readonly DependencyProperty ConverterProperty;
/// <summary>The Converters dependency property which is a collection of Converter.</summary>
public static readonly DependencyProperty ConvertersProperty;
static ConverterService()
{
ConverterProperty = DependencyProperty.RegisterAttached("Converter",
typeof(IConverter),
typeof(ConverterService),
new PropertyMetadata(null));
ConvertersProperty = DependencyProperty.RegisterAttached("Converters",
typeof(IList<IConverter>),
typeof(ConverterService),
new PropertyMetadata(new List<IConverter>()));
}
/// <summary>Property getter for attached property Converter.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <returns>Returns the converter associated with the specified dependency object.</returns>
public static IConverter GetConverter(DependencyObject element)
{
return (IConverter)element.GetValue(ConverterProperty);
}
/// <summary>Property getter for attached property Converter.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <param name="value">The converter to associate.</param>
public static void SetConverter(DependencyObject element, IConverter value)
{
element.SetValue(ConverterProperty, value);
}
/// <summary>Property getter for attached property Converters.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <returns>Returns the collection of converters associated with the specified dependency object.</returns>
public static IList<IConverter> GetConverters(DependencyObject element)
{
return (IList<IConverter>)element.GetValue(ConverterProperty);
}
/// <summary>Property getter for attached property Converters.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <param name="value">The converters to associate.</param>
public static void SetConverters(DependencyObject element, IList<IConverter> value)
{
element.SetValue(ConverterProperty, value);
}
}
/// <summary>A strongly-typed base converter.</summary>
/// <typeparam name="Tin">The input type.</typeparam>
/// <typeparam name="Tout">The output type.</typeparam>
public abstract class BaseConverter<Tin, Tout>: DependencyObject, IConverter
{
/// <summary>The Input dependency property.</summary>
public static readonly DependencyProperty InputProperty;
/// <summary>The Output dependency property.</summary>
public static readonly DependencyProperty OutputProperty;
static BaseConverter()
{
OutputProperty = DependencyProperty.Register("Output",
typeof(Tout),
typeof(BaseConverter<Tin, Tout>),
new PropertyMetadata(GetDefault(typeof(Tout)), OutChanged));
InputProperty = DependencyProperty.Register("Input",
typeof(Tin),
typeof(BaseConverter<Tin, Tout>),
new PropertyMetadata(GetDefault(typeof(Tin)), InChanged));
}
/// <summary>Gets or sets the input value to convert from.</summary>
public Tin Input
{
get { return (Tin)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
/// <summary>Gets or sets the output value to convert to.</summary>
public Tout Output
{
get { return (Tout)GetValue(OutputProperty); }
set { SetValue(OutputProperty, value); }
}
/// <summary>Gets the from type.</summary>
public static Type From
{
get { return typeof(Tin); }
}
/// <summary>Gets the to type.</summary>
public static Type To
{
get { return typeof(Tout); }
}
#region IConverter
object IConverter.Input { get => Input; set => Input = (Tin)value; }
object IConverter.Output { get => Output; set => Output = (Tout)value; }
#endregion
#region IValueConverter
object IValueConverter.Convert(object value, Type targetType, object parameter, string language)
{
return Convert((Tin)value, parameter, language);
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, string language)
{
return ConvertBack((Tout)value, parameter, language);
}
#endregion
/// <summary>Converts an input value into an output value.</summary>
/// <param name="value">The value to convert.</param>
/// <param name="parameter">An optional parameter to pass onto the conversion process (from IValueConverter).</param>
/// <param name="language">An optional language parameter to pass onto the conversion process (from IValueConverter).</param>
/// <returns>Returns the converted value.</returns>
protected abstract Tout Convert(Tin value, object parameter, string language);
/// <summary>Converts back an output value into its original input.</summary>
/// <param name="value">The value to convert.</param>
/// <param name="parameter">An optional parameter to pass onto the conversion process (from IValueConverter).</param>
/// <param name="language">An optional language parameter to pass onto the conversion process (from IValueConverter).</param>
/// <returns>Returns the converted value from output to input.</returns>
protected abstract Tin ConvertBack(Tout value, object parameter, string language);
private static object GetDefault(Type type)
{
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
private static void InChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as BaseConverter<Tin, Tout>;
control.Output = (Tout)((d as IValueConverter).Convert(e.NewValue, typeof(Tout), null, null));
}
private static void OutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as BaseConverter<Tin, Tout>;
//control.Input = (Tin)((d as IValueConverter).ConvertBack(e.NewValue, typeof(Tin), null, null));
}
}
/// <summary>Converts Double to and from GridLength.</summary>
public sealed class DoubleToGridLengthConverter : BaseConverter<double?, GridLength?>
{
/// <summary>The GridUnit dependency property.</summary>
public static readonly DependencyProperty GridUnitProperty;
static DoubleToGridLengthConverter()
{
GridUnitProperty = DependencyProperty.Register("GridUnit",
typeof(GridUnitType),
typeof(DoubleToGridLengthConverter),
new PropertyMetadata(GridUnitType.Auto, UnitChanged));
}
/// <summary>Gets or sets the type of grid unit to be used in the conversions.</summary>
public GridUnitType GridUnit
{
get { return (GridUnitType)GetValue(GridUnitProperty); }
set { SetValue(GridUnitProperty, value); }
}
protected override GridLength? Convert(double? value, object parameter, string language)
{
return value == null || !value.HasValue
? new GridLength()
: new GridLength(value.Value, this.GridUnit);
}
protected override double? ConvertBack(GridLength? value, object parameter, string language)
{
return value == null || !value.HasValue
? default(double?)
: value.Value.Value;
}
private static void UnitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as DoubleToGridLengthConverter;
control.Output = control.Convert(control.Input, null, null);
}
}
And if you want to use my AspectRatio state trigger, here it is:
/// <summary>Aspect ratios.</summary>
public enum AspectRatio
{
/// <summary>Portrait.</summary>
Portrait,
/// <summary>Landscape.</summary>
Landscape
}
/// <summary>A state trigger based on the aspect ratio of the window.</summary>
public class WindowAspectRatioTrigger: StateTriggerBase
{
/// <summary>The target orientation.</summary>
private static readonly DependencyProperty OrientationProperty;
static WindowAspectRatioTrigger()
{
OrientationProperty = DependencyProperty.Register("Orientation",
typeof(AspectRatio),
typeof(WindowAspectRatioTrigger),
new PropertyMetadata(AspectRatio.Landscape, new PropertyChangedCallback(DesiredAspectRatioChanged)));
}
public WindowAspectRatioTrigger()
{
this.OnAspectRatioChanged(this.Orientation);
Window.Current.SizeChanged += Current_SizeChanged;
}
/// <summary>Gets or sets the desired aspect ratio for the trigger.</summary>
public AspectRatio Orientation
{
get { return (AspectRatio)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
private async void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SetActive(IsActive(e.Size, this.Orientation));
});
}
private static void DesiredAspectRatioChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as WindowAspectRatioTrigger;
control.OnAspectRatioChanged((AspectRatio)e.NewValue);
}
private static bool IsActive(Size windowSize, AspectRatio aspectRatio)
{
var currentOrientation = windowSize.Width >= windowSize.Height
? AspectRatio.Landscape
: AspectRatio.Portrait;
return aspectRatio == currentOrientation;
}
private async void OnAspectRatioChanged(AspectRatio aspectRatio)
{
var dimensions = Window.Current.Bounds;
var size = new Size(dimensions.Width, dimensions.Height);
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SetActive(IsActive(size, aspectRatio));
});
}
}
I'm using flipview and bind data. I want to use fadein/out animation when itmes are changed. I'm using DispatcherTimer to change itmes ( _timer.Tick += ChangeImage;).
bind data to flipview
<FlipView x:Name="TheFlipView"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"/>
change function.
private void ChangeItems(object sender, object o)
{
var totalItems = TheFlipView.Items.Count;
var newItemIndex = (TheFlipView.SelectedIndex + 1) % totalItems;
TheFlipView.SelectedIndex = newItemIndex;
}
I tried Storyboard and FadeInThemeAnimation class, but I couldn't ...
Could you help me?
Here's a class from WinRT XAML Toolkit that you can use to fade in/fade out with simple calls like myFlipView.FadeOut(). You could just change your code to something like this:
private async void ChangeItems(object sender, object o)
{
var totalItems = TheFlipView.Items.Count;
var newItemIndex = (TheFlipView.SelectedIndex + 1) % totalItems;
await TheFlipView.FadeOut();
TheFlipView.SelectedIndex = newItemIndex;
TheFlipView.FadeIn();
}
Extensions class:
public static class UIElementAnimationExtensions
{
#region AttachedFadeStoryboard
/// <summary>
/// AttachedFadeStoryboard Attached Dependency Property
/// </summary>
public static readonly DependencyProperty AttachedFadeStoryboardProperty =
DependencyProperty.RegisterAttached(
"AttachedFadeStoryboard",
typeof(Storyboard),
typeof(UIElementAnimationExtensions),
new PropertyMetadata(null, OnAttachedFadeStoryboardChanged));
/// <summary>
/// Gets the AttachedFadeStoryboard property. This dependency property
/// indicates the currently running custom fade in/out storyboard.
/// </summary>
private static Storyboard GetAttachedFadeStoryboard(DependencyObject d)
{
return (Storyboard)d.GetValue(AttachedFadeStoryboardProperty);
}
/// <summary>
/// Sets the AttachedFadeStoryboard property. This dependency property
/// indicates the currently running custom fade in/out storyboard.
/// </summary>
private static void SetAttachedFadeStoryboard(DependencyObject d, Storyboard value)
{
d.SetValue(AttachedFadeStoryboardProperty, value);
}
/// <summary>
/// Handles changes to the AttachedFadeStoryboard property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnAttachedFadeStoryboardChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Storyboard oldAttachedFadeStoryboard = (Storyboard)e.OldValue;
Storyboard newAttachedFadeStoryboard = (Storyboard)d.GetValue(AttachedFadeStoryboardProperty);
}
#endregion
#region FadeIn()
/// <summary>
/// Fades the element in using the FadeInThemeAnimation.
/// </summary>
/// <remarks>
/// Opacity property of the element is not affected.<br/>
/// The duration of the visible animation itself is not affected by the duration parameter. It merely indicates how long the Storyboard will run.<br/>
/// If FadeOutThemeAnimation was not used on the element before - nothing will happen.<br/>
/// </remarks>
/// <param name="element"></param>
/// <param name="duration"></param>
/// <returns></returns>
public static async Task FadeIn(this UIElement element, TimeSpan? duration = null)
{
((FrameworkElement)element).Visibility = Visibility.Visible;
var fadeInStoryboard = new Storyboard();
var fadeInAnimation = new FadeInThemeAnimation();
if (duration != null)
{
fadeInAnimation.Duration = duration.Value;
}
Storyboard.SetTarget(fadeInAnimation, element);
fadeInStoryboard.Children.Add(fadeInAnimation);
await fadeInStoryboard.BeginAsync();
}
#endregion
#region FadeOut()
/// <summary>
/// Fades the element out using the FadeOutThemeAnimation.
/// </summary>
/// <remarks>
/// Opacity property of the element is not affected.<br/>
/// The duration of the visible animation itself is not affected by the duration parameter. It merely indicates how long the Storyboard will run.<br/>
/// If FadeOutThemeAnimation was already run before and FadeInThemeAnimation was not run after that - nothing will happen.<br/>
/// </remarks>
/// <param name="element"></param>
/// <param name="duration"></param>
/// <returns></returns>
public static async Task FadeOut(this UIElement element, TimeSpan? duration = null)
{
var fadeOutStoryboard = new Storyboard();
var fadeOutAnimation = new FadeOutThemeAnimation();
if (duration != null)
{
fadeOutAnimation.Duration = duration.Value;
}
Storyboard.SetTarget(fadeOutAnimation, element);
fadeOutStoryboard.Children.Add(fadeOutAnimation);
await fadeOutStoryboard.BeginAsync();
}
#endregion
#region FadeInCustom()
/// <summary>
/// Fades the element in using a custom DoubleAnimation of the Opacity property.
/// </summary>
/// <param name="element"></param>
/// <param name="duration"></param>
/// <param name="easingFunction"> </param>
/// <returns></returns>
public static async Task FadeInCustom(this UIElement element, TimeSpan? duration = null, EasingFunctionBase easingFunction = null, double targetOpacity = 1.0)
{
CleanUpPreviousFadeStoryboard(element);
var fadeInStoryboard = new Storyboard();
var fadeInAnimation = new DoubleAnimation();
if (duration == null)
duration = TimeSpan.FromSeconds(0.4);
fadeInAnimation.Duration = duration.Value;
fadeInAnimation.To = targetOpacity;
fadeInAnimation.EasingFunction = easingFunction;
Storyboard.SetTarget(fadeInAnimation, element);
Storyboard.SetTargetProperty(fadeInAnimation, "Opacity");
fadeInStoryboard.Children.Add(fadeInAnimation);
SetAttachedFadeStoryboard(element, fadeInStoryboard);
await fadeInStoryboard.BeginAsync();
element.Opacity = targetOpacity;
fadeInStoryboard.Stop();
}
#endregion
#region FadeOutCustom()
/// <summary>
/// Fades the element out using a custom DoubleAnimation of the Opacity property.
/// </summary>
/// <param name="element"></param>
/// <param name="duration"></param>
/// <param name="easingFunction"> </param>
/// <returns></returns>
public static async Task FadeOutCustom(this UIElement element, TimeSpan? duration = null, EasingFunctionBase easingFunction = null)
{
CleanUpPreviousFadeStoryboard(element);
var fadeOutStoryboard = new Storyboard();
var fadeOutAnimation = new DoubleAnimation();
if (duration == null)
duration = TimeSpan.FromSeconds(0.4);
fadeOutAnimation.Duration = duration.Value;
fadeOutAnimation.To = 0.0;
fadeOutAnimation.EasingFunction = easingFunction;
Storyboard.SetTarget(fadeOutAnimation, element);
Storyboard.SetTargetProperty(fadeOutAnimation, "Opacity");
fadeOutStoryboard.Children.Add(fadeOutAnimation);
SetAttachedFadeStoryboard(element, fadeOutStoryboard);
await fadeOutStoryboard.BeginAsync();
element.Opacity = 0.0;
fadeOutStoryboard.Stop();
}
#endregion
#region CleanUpPreviousFadeStoryboard()
public static void CleanUpPreviousFadeStoryboard(this UIElement element)
{
var attachedFadeStoryboard = GetAttachedFadeStoryboard(element);
if (attachedFadeStoryboard != null)
{
attachedFadeStoryboard.Stop();
}
}
#endregion
}
The goal of this answer is to complement Filip Skakun's answer since the extension BeginAsync was missed. After embedding this extension as shown below into his class UIElementAnimationExtensions, it works fine for my case. ^_^
public async static Task BeginAsync(this Storyboard myStoryboard)
{
SemaphoreSlim signal = new SemaphoreSlim(0, 1);
EventHandler<object> eventHandler = new EventHandler<object>(
(sender, args) =>
{
signal.Release();
}
);
myStoryboard.Completed += eventHandler;
myStoryboard.Begin();
await signal.WaitAsync();
myStoryboard.Completed -= eventHandler;
}
By the way, the concept of this extension is based on the answer of Is it possible to await an event instead of another async method?.
My combobox returns a set of values from s stored procedure as this
private void BindCombo()
{
DataCombo.FillCombo(ComboDS(2313001), cmbClass, 0);
DataCombo.FillCombo(DDCombo(5007), cmbGroup, 0);
}
I managed to give a rudimentary auto complete suggestion as IsTextSearchenabled but cannot get a auto suggestion box that i would like.
I have seen loads of examples of autocomplete/suggestive textboxes but none of them seem to suit me.
this code apparently suits me.
but how would i use the auto suggest here
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace DotNetZen.AutoFilteredComboBox
{
public class AutoFilteredComboBox : ComboBox
{
private int silenceEvents = 0;
/// <summary>
/// Creates a new instance of <see cref="AutoFilteredComboBox" />.
/// </summary>
public AutoFilteredComboBox()
{
DependencyPropertyDescriptor textProperty = DependencyPropertyDescriptor.FromProperty(
ComboBox.TextProperty, typeof(AutoFilteredComboBox));
textProperty.AddValueChanged(this, this.OnTextChanged);
this.RegisterIsCaseSensitiveChangeNotification();
}
#region IsCaseSensitive Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="IsCaseSensitive" /> dependency property.
/// </summary>
public static readonly DependencyProperty IsCaseSensitiveProperty =
DependencyProperty.Register("IsCaseSensitive", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(false));
/// <summary>
/// Gets or sets the way the combo box treats the case sensitivity of typed text.
/// </summary>
/// <value>The way the combo box treats the case sensitivity of typed text.</value>
[System.ComponentModel.Description("The way the combo box treats the case sensitivity of typed text.")]
[System.ComponentModel.Category("AutoFiltered ComboBox")]
[System.ComponentModel.DefaultValue(true)]
public bool IsCaseSensitive
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(IsCaseSensitiveProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(IsCaseSensitiveProperty, value);
}
}
protected virtual void OnIsCaseSensitiveChanged(object sender, EventArgs e)
{
if (this.IsCaseSensitive)
this.IsTextSearchEnabled = false;
this.RefreshFilter();
}
private void RegisterIsCaseSensitiveChangeNotification()
{
System.ComponentModel.DependencyPropertyDescriptor.FromProperty(IsCaseSensitiveProperty, typeof(AutoFilteredComboBox)).AddValueChanged(
this, this.OnIsCaseSensitiveChanged);
}
#endregion
#region DropDownOnFocus Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="DropDownOnFocus" /> dependency property.
/// </summary>
public static readonly DependencyProperty DropDownOnFocusProperty =
DependencyProperty.Register("DropDownOnFocus", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(true));
/// <summary>
/// Gets or sets the way the combo box behaves when it receives focus.
/// </summary>
/// <value>The way the combo box behaves when it receives focus.</value>
[System.ComponentModel.Description("The way the combo box behaves when it receives focus.")]
[System.ComponentModel.Category("AutoFiltered ComboBox")]
[System.ComponentModel.DefaultValue(true)]
public bool DropDownOnFocus
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(DropDownOnFocusProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(DropDownOnFocusProperty, value);
}
}
#endregion
#region | Handle selection |
/// <summary>
/// Called when <see cref="ComboBox.ApplyTemplate()"/> is called.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.EditableTextBox.SelectionChanged += this.EditableTextBox_SelectionChanged;
}
/// <summary>
/// Gets the text box in charge of the editable portion of the combo box.
/// </summary>
protected TextBox EditableTextBox
{
get
{
return ((TextBox)base.GetTemplateChild("PART_EditableTextBox"));
}
}
private int start = 0, length = 0;
private void EditableTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if (this.silenceEvents == 0)
{
this.start = ((TextBox)(e.OriginalSource)).SelectionStart;
this.length = ((TextBox)(e.OriginalSource)).SelectionLength;
this.RefreshFilter();
}
}
#endregion
#region | Handle focus |
/// <summary>
/// Invoked whenever an unhandled <see cref="UIElement.GotFocus" /> event
/// reaches this element in its route.
/// </summary>
/// <param name="e">The <see cref="RoutedEventArgs" /> that contains the event data.</param>
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (this.ItemsSource != null && this.DropDownOnFocus)
{
this.IsDropDownOpen = true;
}
}
#endregion
#region | Handle filtering |
private void RefreshFilter()
{
if (this.ItemsSource != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.Refresh();
this.IsDropDownOpen = true;
}
}
private bool FilterPredicate(object value)
{
// We don't like nulls.
if (value == null)
return false;
// If there is no text, there's no reason to filter.
if (this.Text.Length == 0)
return true;
string prefix = this.Text;
// If the end of the text is selected, do not mind it.
if (this.length > 0 && this.start + this.length == this.Text.Length)
{
prefix = prefix.Substring(0, this.start);
}
return value.ToString()
.StartsWith(prefix, !this.IsCaseSensitive, CultureInfo.CurrentCulture);
}
#endregion
/// <summary>
/// Called when the source of an item in a selector changes.
/// </summary>
/// <param name="oldValue">Old value of the source.</param>
/// <param name="newValue">New value of the source.</param>
protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
{
if (newValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
view.Filter += this.FilterPredicate;
}
if (oldValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
view.Filter -= this.FilterPredicate;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
private void OnTextChanged(object sender, EventArgs e)
{
if (!this.IsTextSearchEnabled && this.silenceEvents == 0)
{
this.RefreshFilter();
// Manually simulate the automatic selection that would have been
// available if the IsTextSearchEnabled dependency property was set.
if (this.Text.Length > 0)
{
foreach (object item in CollectionViewSource.GetDefaultView(this.ItemsSource))
{
int text = item.ToString().Length, prefix = this.Text.Length;
this.SelectedItem = item;
this.silenceEvents++;
this.EditableTextBox.Text = item.ToString();
this.EditableTextBox.Select(prefix, text - prefix);
this.silenceEvents--;
break;
}
}
}
}
}
}
Also found the AutoFilteredComboBox to be very simple to work with. Though I have made some changes:
Removed use of DependencyPropertyDescriptor to avoid memory leak of combobox-objects
Introduced FilterItem-event and FilterList-event to allow one to customize the filtering
Changed default filtering from starts-with-string to contains-string
Removed support of having IsTextSearchEnabled enabled
Shows dropdown as soon one changes the search string, so search result is displayed
Example of how it is used:
<Controls:AutoFilteredComboBox ItemsSource="{Binding ViewModel.AvailableItems}"
SelectedValue="{Binding ViewModel.SelectedItem, Mode=TwoWay}"
IsEditable="True" IsTextSearchEnabled="False"/>
Improved version of AutoFilteredComboBox:
public class AutoFilteredComboBox : ComboBox
{
bool _ignoreTextChanged;
string _currentText;
/// <summary>
/// Creates a new instance of <see cref="AutoFilteredComboBox" />.
/// </summary>
public AutoFilteredComboBox()
{
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) return;
}
public event Func<object, string, bool> FilterItem;
public event Action<string> FilterList;
#region IsCaseSensitive Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="IsCaseSensitive" /> dependency property.
/// </summary>
public static readonly DependencyProperty IsCaseSensitiveProperty =
DependencyProperty.Register("IsCaseSensitive", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(false));
/// <summary>
/// Gets or sets the way the combo box treats the case sensitivity of typed text.
/// </summary>
/// <value>The way the combo box treats the case sensitivity of typed text.</value>
[Description("The way the combo box treats the case sensitivity of typed text.")]
[Category("AutoFiltered ComboBox")]
[DefaultValue(true)]
public bool IsCaseSensitive
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(IsCaseSensitiveProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(IsCaseSensitiveProperty, value);
}
}
#endregion
#region DropDownOnFocus Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="DropDownOnFocus" /> dependency property.
/// </summary>
public static readonly DependencyProperty DropDownOnFocusProperty =
DependencyProperty.Register("DropDownOnFocus", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(false));
/// <summary>
/// Gets or sets the way the combo box behaves when it receives focus.
/// </summary>
/// <value>The way the combo box behaves when it receives focus.</value>
[Description("The way the combo box behaves when it receives focus.")]
[Category("AutoFiltered ComboBox")]
[DefaultValue(false)]
public bool DropDownOnFocus
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(DropDownOnFocusProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(DropDownOnFocusProperty, value);
}
}
#endregion
#region | Handle focus |
/// <summary>
/// Invoked whenever an unhandled <see cref="UIElement.GotFocus" /> event
/// reaches this element in its route.
/// </summary>
/// <param name="e">The <see cref="RoutedEventArgs" /> that contains the event data.</param>
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (this.ItemsSource != null && this.DropDownOnFocus)
{
this.IsDropDownOpen = true;
}
}
#endregion
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
AddHandler(TextBox.TextChangedEvent, new TextChangedEventHandler(OnTextChanged));
KeyUp += AutoFilteredComboBox_KeyUp;
this.IsTextSearchEnabled = false;
}
void AutoFilteredComboBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down)
{
if (this.IsDropDownOpen == true)
{
// Ensure that focus is given to the dropdown list
if (Keyboard.FocusedElement is TextBox)
{
Keyboard.Focus(this);
if (this.Items.Count > 0)
{
if (this.SelectedIndex == -1 || this.SelectedIndex==0)
this.SelectedIndex = 0;
}
}
}
}
if (Keyboard.FocusedElement is TextBox)
{
if (e.OriginalSource is TextBox)
{
// Avoid the automatic selection of the first letter (As next letter will cause overwrite)
TextBox textBox = e.OriginalSource as TextBox;
if (textBox.Text.Length == 1 && textBox.SelectionLength == 1)
{
textBox.SelectionLength = 0;
textBox.SelectionStart = 1;
}
}
}
}
#region | Handle filtering |
private void RefreshFilter()
{
if (this.ItemsSource != null)
{
Action<string> filterList = FilterList;
if (filterList != null)
{
filterList(_currentText);
}
else
{
ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.Refresh();
}
this.SelectedIndex = -1; // Prepare so arrow down selects first
this.IsDropDownOpen = true;
}
}
private bool FilterPredicate(object value)
{
// We don't like nulls.
if (value == null)
return false;
// If there is no text, there's no reason to filter.
if (string.IsNullOrEmpty(_currentText))
return true;
Func<object, string, bool> filterItem = FilterItem;
if (filterItem != null)
return filterItem(value, _currentText);
if (IsCaseSensitive)
return value.ToString().Contains(_currentText);
else
return value.ToString().ToUpper().Contains(_currentText.ToUpper());
}
#endregion
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
try
{
_ignoreTextChanged = true; // Ignore the following TextChanged
base.OnSelectionChanged(e);
}
finally
{
_ignoreTextChanged = false;
}
}
/// <summary>
/// Called when the source of an item in a selector changes.
/// </summary>
/// <param name="oldValue">Old value of the source.</param>
/// <param name="newValue">New value of the source.</param>
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (newValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
if (FilterList == null)
view.Filter += this.FilterPredicate;
}
if (oldValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
view.Filter -= this.FilterPredicate;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (_ignoreTextChanged)
return;
_currentText = Text;
if (!this.IsTextSearchEnabled)
{
this.RefreshFilter();
}
}
I have found a super simple workaround to my problem.
i created a preview text input event of the combobox.
then i just wrote
Combobox.IsDropDownOpen = true
may not the most elegant but works in my case
I have made a GIF Animation user control which loads gif animations and have created about 30 of them in my project each animating a GIF. The problem is that when I check my CPU usage it is about 70%+!!! I'm sure something is wrong with this!
Please help me. Here is the code of that GIF Animator control:
public class AnimatedImage : System.Windows.Controls.Image
{
private BitmapSource[] _BitmapSources = null;
private int _nCurrentFrame=0;
private bool _bIsAnimating=false;
public bool IsAnimating
{
get { return _bIsAnimating; }
}
static AnimatedImage()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AnimatedImage), new FrameworkPropertyMetadata(typeof(AnimatedImage)));
}
public Bitmap AnimatedBitmap
{
get { return (Bitmap)GetValue(AnimatedBitmapProperty); }
set { StopAnimate(); SetValue(AnimatedBitmapProperty, value); }
}
/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty AnimatedBitmapProperty =
DependencyProperty.Register(
"AnimatedBitmap", typeof(Bitmap), typeof(AnimatedImage),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnAnimatedBitmapChanged)));
private static void OnAnimatedBitmapChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
AnimatedImage control = (AnimatedImage)obj;
control.UpdateAnimatedBitmap();
RoutedPropertyChangedEventArgs<Bitmap> e = new RoutedPropertyChangedEventArgs<Bitmap>(
(Bitmap)args.OldValue, (Bitmap)args.NewValue, AnimatedBitmapChangedEvent);
control.OnAnimatedBitmapChanged(e);
}
/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent AnimatedBitmapChangedEvent = EventManager.RegisterRoutedEvent(
"AnimatedBitmapChanged", RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<Bitmap>), typeof(AnimatedImage));
/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<Bitmap> AnimatedBitmapChanged
{
add { AddHandler(AnimatedBitmapChangedEvent, value); }
remove { RemoveHandler(AnimatedBitmapChangedEvent, value); }
}
/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnAnimatedBitmapChanged(RoutedPropertyChangedEventArgs<Bitmap> args)
{
RaiseEvent(args);
}
private void UpdateAnimatedBitmap()
{
int nTimeFrames = AnimatedBitmap.GetFrameCount(System.Drawing.Imaging.FrameDimension.Time);
_nCurrentFrame = 0;
if (nTimeFrames > 0)
{
_BitmapSources = new BitmapSource[nTimeFrames];
for (int i = 0; i < nTimeFrames; i++)
{
AnimatedBitmap.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Time, i);
Bitmap bitmap = new Bitmap(AnimatedBitmap);
bitmap.MakeTransparent();
_BitmapSources[i] = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
StartAnimate();
}
}
private delegate void VoidDelegate();
private void OnFrameChanged(object o, EventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Render, new VoidDelegate(delegate { ChangeSource(); }));
}
void ChangeSource()
{
Source = _BitmapSources[_nCurrentFrame++];
_nCurrentFrame = _nCurrentFrame % _BitmapSources.Length;
ImageAnimator.UpdateFrames();
}
public void StopAnimate()
{
if (_bIsAnimating)
{
ImageAnimator.StopAnimate(AnimatedBitmap, new EventHandler(this.OnFrameChanged));
_bIsAnimating = false;
}
}
public void StartAnimate()
{
if (!_bIsAnimating)
{
ImageAnimator.Animate(AnimatedBitmap, new EventHandler(this.OnFrameChanged));
_bIsAnimating = true;
}
}
}
}
Give this one a shot. We've used it in our application and I haven't notice any weird memory consumption.