Animate DataGridRow with Behavior - c#

I need to animate a row in certain circumstances. I have a Behaivor that only runs when a certain property changes. The problem I have is that when the behaivor ends, I lose all the animations I had before. Watch the mouse over before and after the animation. Also, when finished, observe when I click on the row. Any solution?
.gif
public enum Events
{
Changed
}
public class AnimateBehavior : Behavior<TextBlock>
{
public Events Property { get; set; }
private ColorAnimation ColorAnimation { get; set; }
public Brush BackgroundBrush { get; set; }
protected override void OnAttached()
{
ColorAnimation = new ColorAnimation
{
AutoReverse = true,
To = Colors.LightBlue,
From = Colors.Transparent,
FillBehavior = FillBehavior.Stop,
RepeatBehavior = new RepeatBehavior(3),
Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
AssociatedObject.DataContextChanged += OnDataContextChanged;
WireEvents();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is INotifyPropertyChanged oldData)
{
oldData.PropertyChanged -= OnPropertyChanged;
}
WireEvents();
}
private void WireEvents()
{
if (AssociatedObject.DataContext is INotifyPropertyChanged currentData)
{
currentData.PropertyChanged += OnPropertyChanged;
}
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (Property == Events.Changed)
{
var dataGridRow = AssociatedObject.TryFindParent<DataGridRow>();
//ColorAnimation.Completed += (s, x) =>
//{
// dataGridRow.Background = Brushes.Transparent;
//};
dataGridRow.Background =
(SolidColorBrush) new BrushConverter().ConvertFromString(dataGridRow.Background.ToString());
dataGridRow.Background?
.BeginAnimation(SolidColorBrush.ColorProperty, ColorAnimation);
}
}
protected override void OnDetaching()
{
if (AssociatedObject.DataContext is INotifyPropertyChanged currentData)
{
currentData.PropertyChanged -= OnPropertyChanged;
}
}
}
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastUpdated}">
<Interactivity:Interaction.Behaviors>
<customBehavior:AnimateBehavior Property="Changed" />
</Interactivity:Interaction.Behaviors>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>

Related

WPF MVVM show hidden window and set focus

I would like to show/activate a hidden window and set the (keyboard-)focus to a textbox programmatically (without violating mvvm if possible).
So if the the user presses a hotkey (already implemented) following should happen:
Window gets shown + activated
Textbox gets focused + keyboard focus (so the user can type some text)
Pressing the hotkey again should hide the window.
I tried binding Activated and Visibility to a boolean value, but I ran into some problems with TwoWay-Binding. Thank you for any ideas on how to solve this problem.
Edit:
MainWindow.xml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>
<i:Interaction.Behaviors>
<bh:ActivateBehavior Activated="{Binding Visible, Mode=TwoWay}"/>
</i:Interaction.Behaviors>
<Window.Visibility>
<Binding Path="Visible" Mode="TwoWay" Converter="{StaticResource BoolToVisConverter}"/>
</Window.Visibility>
<TextBox Grid.Column="1" x:Name="SearchBar" bh:FocusExtension.IsFocused="{Binding Visible, Mode=TwoWay}"/>
ActivateBehavior.cs:
used from this https://stackoverflow.com/a/12254217/13215602
public class ActivateBehavior : Behavior<Window> {
Boolean isActivated;
public static readonly DependencyProperty ActivatedProperty =
DependencyProperty.Register(
"Activated",
typeof(Boolean),
typeof(ActivateBehavior),
new PropertyMetadata(OnActivatedChanged)
);
public Boolean Activated {
get { return (Boolean) GetValue(ActivatedProperty); }
set { SetValue(ActivatedProperty, value); }
}
static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
var behavior = (ActivateBehavior) dependencyObject;
if (!behavior.Activated || behavior.isActivated)
return;
// The Activated property is set to true but the Activated event (tracked by the
// isActivated field) hasn't been fired. Go ahead and activate the window.
if (behavior.AssociatedObject.WindowState == WindowState.Minimized)
behavior.AssociatedObject.WindowState = WindowState.Normal;
behavior.AssociatedObject.Activate();
}
protected override void OnAttached() {
AssociatedObject.Activated += OnActivated;
AssociatedObject.Deactivated += OnDeactivated;
}
protected override void OnDetaching() {
AssociatedObject.Activated -= OnActivated;
AssociatedObject.Deactivated -= OnDeactivated;
}
void OnActivated(Object sender, EventArgs eventArgs) {
this.isActivated = true;
Activated = true;
}
void OnDeactivated(Object sender, EventArgs eventArgs) {
this.isActivated = false;
Activated = false;
}
}
FocusExtension.cs: used from this https://stackoverflow.com/a/1356781/13215602
public static class FocusExtension
{
public static bool GetIsFocuses(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement) d;
if ((bool) e.NewValue)
{
Keyboard.Focus(uie);
uie.Focus();
}
}
}
MainViewModel.cs:
private bool mVisible;
//Binding to this variable
public bool Visible
{
get { return mVisible; }
set
{
if (mVisible == value)
return;
mVisible = value;
RaisePropertyChanged(nameof(Visible));
}
}

How to prevent ScrollViewer firing ViewChanged event while it is updating?

I have two ScrollViewer and I need to sync position of those ScrollViewer when any of the ScrollViewer gets changes, but right now suppose when anyone scrollviewer2 is changed then on calling ChangeView event of ScrollViewer1 it's firing its ViewChangedEvent which is resetting ScrollViewer2 position back.
private void Scroll(ScrollViewer changedScrollViewer)
{
var group = ScrollViewers[changedScrollViewer];
VerticalScrollOffsets[group] = changedScrollViewer.VerticalOffset;
HorizontalScrollOffsets[group] = changedScrollViewer.HorizontalOffset;
foreach (var scrollViewer in ScrollViewers.Where(s => s.Value == group && s.Key != changedScrollViewer))
{
scrollViewer.Key.ViewChanged -= ScrollViewer_ViewChanged;
if (scrollViewer.Key.VerticalOffset != changedScrollViewer.VerticalOffset)
{
scrollViewer.Key.ChangeView(null, changedScrollViewer.VerticalOffset, null, true);
}
if (scrollViewer.Key.HorizontalOffset != changedScrollViewer.HorizontalOffset)
{
scrollViewer.Key.ChangeView(changedScrollViewer.HorizontalOffset, null, null, true);
}
//Commenting this line works. But I need to set ViewChange event back.
scrollViewer.Key.ViewChanged += ScrollViewer_ViewChanged;
}
}
#Nico's solution is much preferable. If you still need something with a flag, it 'll look like this:
bool is_programmatic_call = false;
private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (is_programmatic_call)
{
is_programmatic_call = false;
return;
}
if(sender == ScrollViewer1)
{
ScrollViewer2.ViewChanged -= ScrollViewer_ViewChanged;
is_programmatic_call = true;
ScrollViewer2.ChangeView(ScrollViewer1.HorizontalOffset, ScrollViewer1.VerticalOffset, null, true);
ScrollViewer2.ViewChanged += ScrollViewer_ViewChanged;
}
else
{
ScrollViewer1.ViewChanged -= ScrollViewer_ViewChanged;
is_programmatic_call = true;
ScrollViewer1.ChangeView(ScrollViewer2.HorizontalOffset, ScrollViewer2.VerticalOffset, null, true);
ScrollViewer1.ViewChanged += ScrollViewer_ViewChanged;
}
}
both the ScrollViewer's ViewChanged event is handled by this ScrollViewer_ViewChanged
For sync two ScrollViewers, the better way is make a new Dependency Property, and bind it with the same value. It will notify the ScrollViewer to scroll automatically when the Dependency Property value changed. This solution will stop Circular Reference happening in the ViewChanged event.
I have implemented it for ListView in this code sample. You could refer segment code. But for ScrollViewer, you need to make xaml Behavior, because ScrollViewer is sealed class, it could not be inherited.
public class SyncBehavior : Behavior<ScrollViewer>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnAssociatedObjectLoaded;
AssociatedObject.LayoutUpdated += OnAssociatedObjectLayoutUpdated;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated;
}
private void OnAssociatedObjectLayoutUpdated(object sender, object o)
{
SyncPointOffSetY();
}
private void OnAssociatedObjectLoaded(object sender, RoutedEventArgs routedEventArgs)
{
SyncPointOffSetY();
AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
}
private void SyncPointOffSetY()
{
if (AssociatedObject == null) return;
AssociatedObject.ViewChanged += AssociatedObject_ViewChanged;
}
private void AssociatedObject_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var MyScrollViewer = sender as ScrollViewer;
this.SetValue(PointOffSetYProperty, MyScrollViewer.VerticalOffset);
}
public double PointOffSetY
{
get { return (double)GetValue(PointOffSetYProperty); }
set { SetValue(PointOffSetYProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointOffSetYProperty =
DependencyProperty.Register("PointOffSetY", typeof(double), typeof(SyncBehavior), new PropertyMetadata(0.0, CallBack));
private static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var current = d as SyncBehavior;
var temScrollViewer = current.AssociatedObject;
if (e.NewValue != e.OldValue & (double)e.NewValue != 0)
{
temScrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
}
}
Usage
<ScrollViewer >
<Interactivity:Interaction.Behaviors>
<local:SyncBehavior PointOffSetY="{Binding PointY,Mode=TwoWay}"/>
</Interactivity:Interaction.Behaviors>
<StackPanel >
<Rectangle Height="500" Fill="Red"/>
<Rectangle Height="500" Fill="Black"/>
<Rectangle Height="500" Fill="Yellow"/>
</StackPanel>
</ScrollViewer>
<ScrollViewer Grid.Column="1" >
<Interactivity:Interaction.Behaviors>
<local:SyncBehavior PointOffSetY="{Binding PointY,Mode=TwoWay}"/>
</Interactivity:Interaction.Behaviors>
<StackPanel >
<Rectangle Height="500" Fill="Red"/>
<Rectangle Height="500" Fill="Black"/>
<Rectangle Height="500" Fill="Yellow"/>
</StackPanel>
</ScrollViewer>
And I have also added the above code to the sample that you could refer easily.

UWP CommandBar Binding

is it possible to bind an UWP CommandBar to something like a ObservableCollection or so?
What i want to achieve ist to bind my CommandBar of my NavigationView to an Object of a specific Page so that the AppBarButton change dynamicaly depending on the current Page
What i tryed:
MainPage.xaml
<NavigationView.HeaderTemplate>
<DataTemplate>
<Grid>
<CommandBar Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
DefaultLabelPosition="Right"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}" Content="{Binding Path=Content.AppBarButtonList, ElementName=rootFrame}">
</CommandBar>
</Grid>
</DataTemplate>
</NavigationView.HeaderTemplate>
SomePage.xaml.cs
public ObservableCollection<AppBarButton> AppBarButtonList = new ObservableCollection<AppBarButton> {
new AppBarButton { Icon = new SymbolIcon(Symbol.Accept), Label="Bla" },
new AppBarButton{Icon=new SymbolIcon(Symbol.Add),Label="Add"}
};
But the CommandBar shows nothing.
Thanks.
My original solution was using the PrimaryCommands property to bind the commands, but it turns out this property is read-only.
My solution to the problem will be using behaviors.
First add a reference to Microsoft.Xaml.Behaviors.Uwp.Managed from NuGet.
Then add the following behavior to your project:
public class BindableCommandBarBehavior : Behavior<CommandBar>
{
public ObservableCollection<AppBarButton> PrimaryCommands
{
get { return (ObservableCollection<AppBarButton>)GetValue(PrimaryCommandsProperty); }
set { SetValue(PrimaryCommandsProperty, value); }
}
public static readonly DependencyProperty PrimaryCommandsProperty = DependencyProperty.Register(
"PrimaryCommands", typeof(ObservableCollection<AppBarButton>), typeof(BindableCommandBarBehavior), new PropertyMetadata(default(ObservableCollection<AppBarButton>), UpdateCommands));
private static void UpdateCommands(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is BindableCommandBarBehavior behavior)) return;
var oldList = dependencyPropertyChangedEventArgs.OldValue as ObservableCollection<AppBarButton>;
if (dependencyPropertyChangedEventArgs.OldValue != null)
{
oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged;
}
var newList = dependencyPropertyChangedEventArgs.NewValue as ObservableCollection<AppBarButton>;
if (dependencyPropertyChangedEventArgs.NewValue != null)
{
newList.CollectionChanged += behavior.PrimaryCommandsCollectionChanged;
}
behavior.UpdatePrimaryCommands();
}
private void PrimaryCommandsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
UpdatePrimaryCommands();
}
private void UpdatePrimaryCommands()
{
if (PrimaryCommands != null)
{
AssociatedObject.PrimaryCommands.Clear();
foreach (var command in PrimaryCommands)
{
AssociatedObject.PrimaryCommands.Add(command);
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
if (PrimaryCommands != null)
{
PrimaryCommands.CollectionChanged -= PrimaryCommandsCollectionChanged;
}
}
}
This behavior essentially creates a fake PrimaryCommands property that is bindable and also observes collection changed events. Whenever a change occurs, the commands are rebuilt.
Finally, the problem in your code is that your AppBarButtonList is just a field, not a property. Change it like this:
public ObservableCollection<AppBarButton> AppBarButtonList { get; } = new ObservableCollection<AppBarButton> {
new AppBarButton { Icon = new SymbolIcon(Symbol.Accept), Label="Bla" },
new AppBarButton{Icon=new SymbolIcon(Symbol.Add),Label="Add"}
};
Notice the {get ;} which was added before the assignment operator.
Now you can use the behavior in XAML like this:
<CommandBar>
<interactivity:Interaction.Behaviors>
<local:BindableCommandBarBehavior PrimaryCommands="{Binding Path=Content.AppBarButtonList, ElementName=rootFrame}" />
</interactivity:Interaction.Behaviors>
</CommandBar>
This is by no means a perfect solution and could be improved upon to allow different collection types binding and more, but it should cover your scenario. An alternative solution would be to implement a custom version of command bar, with new additional dependency property directly on the type, but I used behavior to make it clearer for the user that this is an "added" functionality, not a built-in one.
I found this answer very helpful. I did some more adjustments, like using a DataTemplateSelector to remove UI references like "AppBarButton" from the bindable data source.
public class BindableCommandBarBehavior : Behavior<CommandBar>
{
public static readonly DependencyProperty PrimaryCommandsProperty = DependencyProperty.Register(
"PrimaryCommands", typeof(object), typeof(BindableCommandBarBehavior),
new PropertyMetadata(null, UpdateCommands));
public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register(
"ItemTemplateSelector", typeof(DataTemplateSelector), typeof(BindableCommandBarBehavior),
new PropertyMetadata(null, null));
public DataTemplateSelector ItemTemplateSelector
{
get { return (DataTemplateSelector)GetValue(ItemTemplateSelectorProperty); }
set { SetValue(ItemTemplateSelectorProperty, value); }
}
public object PrimaryCommands
{
get { return GetValue(PrimaryCommandsProperty); }
set { SetValue(PrimaryCommandsProperty, value); }
}
protected override void OnDetaching()
{
base.OnDetaching();
if (PrimaryCommands is INotifyCollectionChanged notifyCollectionChanged)
{
notifyCollectionChanged.CollectionChanged -= PrimaryCommandsCollectionChanged;
}
}
private void UpdatePrimaryCommands()
{
if (AssociatedObject == null)
return;
if (PrimaryCommands == null)
return;
AssociatedObject.PrimaryCommands.Clear();
if (!(PrimaryCommands is IEnumerable enumerable))
{
AssociatedObject.PrimaryCommands.Clear();
return;
}
foreach (var command in enumerable)
{
var template = ItemTemplateSelector.SelectTemplate(command, AssociatedObject);
if (!(template?.LoadContent() is FrameworkElement dependencyObject))
continue;
dependencyObject.DataContext = command;
if (dependencyObject is ICommandBarElement icommandBarElement)
AssociatedObject.PrimaryCommands.Add(icommandBarElement);
}
}
protected override void OnAttached()
{
base.OnAttached();
UpdatePrimaryCommands();
}
private void PrimaryCommandsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdatePrimaryCommands();
}
private static void UpdateCommands(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is BindableCommandBarBehavior behavior)) return;
if (dependencyPropertyChangedEventArgs.OldValue is INotifyCollectionChanged oldList)
{
oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged;
}
if (dependencyPropertyChangedEventArgs.NewValue is INotifyCollectionChanged newList)
{
newList.CollectionChanged += behavior.PrimaryCommandsCollectionChanged;
}
behavior.UpdatePrimaryCommands();
}
}
The DataTemplateSelector:
public class CommandBarMenuItemTemplateSelector : DataTemplateSelector
{
public DataTemplate CbMenuItemTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item is ContextAction)
{
return CbMenuItemTemplate;
}
return base.SelectTemplateCore(item, container);
}
}
Xaml for templates:
<DataTemplate x:Key="CbMenuItemTemplate">
<AppBarButton
Command="{Binding Command}"
Icon="Add"
Label="{Binding Text}" />
</DataTemplate>
<viewLogic:CommandBarMenuItemTemplateSelector x:Key="CommandBarMenuItemTemplateSelector"
CbMenuItemTemplate="{StaticResource CbMenuItemTemplate}" />
Usage:
<CommandBar>
<interactivity:Interaction.Behaviors>
<viewLogic:BindableCommandBarBehavior ItemTemplateSelector="{StaticResource CommandBarMenuItemTemplateSelector}" PrimaryCommands="{Binding ContextActions}" />
</interactivity:Interaction.Behaviors>
</CommandBar>
Where ContextActions is a ObservableCollection of my class ContextAction.

Move-able Expander

I am trying to create a user move-able expander for my WPF project. Ideally, if the user clicks the expander button, s/he should be able to expand. If one clicks and drags the text box that say "Advanced Options" I want that person to be able to move the expander around. My code doesn't work and just expands the expander. I tried associating the click on the header with something different than expanding, but that doesn't seem to work. The xaml and C# code is below. Help?
<Expander HorizontalAlignment="Left" Margin="89,372,0,0" VerticalAlignment="Top" Height="453" Width="909" IsExpanded="True" x:Name="grid_expander">
<Expander.Header>
<TextBlock HorizontalAlignment="Left" Height="22" Margin="5,0,0,0" TextWrapping="Wrap" Width="154" Foreground="White" FontSize="18" MouseDown="grid_expander_MouseDown" MouseUp="grid_expander_MouseUp" MouseMove="grid_expander_MouseMove">
Advanced Options
</TextBlock>
</Expander.Header>
<Expander.RenderTransform>
<TranslateTransform x:Name="expander_xy"/>
</Expander.RenderTransform>
</Expander>
private void grid_expander_MouseDown(object sender, MouseButtonEventArgs e)
{
//e.Handled = true;
m_start = e.GetPosition(window);
m_startOffset = new Vector(expander_xy.X, expander_xy.Y);
grid_expander.CaptureMouse();
}
private void grid_expander_MouseMove(object sender, MouseEventArgs e)
{
if (grid_expander.IsMouseCaptured)
{
Vector offset = Point.Subtract(e.GetPosition(window), m_start);
expander_xy.X = m_startOffset.X + offset.X;
expander_xy.Y = m_startOffset.Y + offset.Y;
}
}
private void grid_expander_MouseUp(object sender, MouseButtonEventArgs e)
{
grid_expander.ReleaseMouseCapture();
}
I have did the same using an Image Control instead of Expander Control, I believe you can replace the Image Control and place your Expander Control I will share my code. Try doing with Expander Control Let me know if it helps.
XAML
<Grid
Background="Black"
mousebehav:MouseBehaviour.MouseLeftButtonUpCommand="{Binding MLBUCommand}">
<Canvas >
<Image RenderTransformOrigin="0.5,0.5"
Source="{Binding ImageSource}"
Stretch="None" SnapsToDevicePixels="False"
mousebehav:MouseBehaviour.MouseLeftButtonDownCommand="{Binding MDCommand}"
mousebehav:MouseBehaviour.MouseMoveCommand="{Binding MMCommand}">
<Image.RenderTransform>
<TranslateTransform
X="{Binding MouseX}"
Y="{Binding MouseY}" />
</Image.RenderTransform>
<Image.LayoutTransform>
<TransformGroup>
<ScaleTransform
ScaleX="{Binding ScaleX}"
ScaleY="{Binding ScaleY}"/>
<RotateTransform
Angle="{Binding RotateAngle}"/>
</TransformGroup>
</Image.LayoutTransform>
</Image>
</Canvas>
</Grid>
View Model
private double _ScaleX;
public double ScaleX
{
get { return _ScaleX; }
set { _ScaleX = value; NotifyPropertyChanged(); }
}
private double _ScaleY;
public double ScaleY
{
get { return _ScaleY; }
set { _ScaleY = value; NotifyPropertyChanged(); }
}
private double _RotateAngle;
public double RotateAngle {
get { return _RotateAngle; }
set { _RotateAngle = value; NotifyPropertyChanged(); }
}
private double _MouseX;
public double MouseX
{
get { return _MouseX; }
set { _MouseX = value; NotifyPropertyChanged(); }
}
private double _MouseY;
public double MouseY
{
get { return _MouseY; }
set { _MouseY = value; NotifyPropertyChanged(); }
}
public bool IsMouseCaptured { get; set; }
private RelayCommand _mouseDownCommand;
public RelayCommand MDCommand
{
get
{
if (_mouseDownCommand == null) return _mouseDownCommand = new RelayCommand(param => ExecuteMouseDown((MouseEventArgs)param));
return _mouseDownCommand;
}
set { _mouseDownCommand = value; }
}
System.Windows.Point start;
System.Windows.Point origin;
private void ExecuteMouseDown(MouseEventArgs e)
{
var image = (System.Windows.Controls.Image)e.OriginalSource;
var border = (IInputElement)image.Parent;
start = e.GetPosition(border);
origin = new System.Windows.Point(MouseX, MouseY);
IsMouseCaptured = true;
}
private RelayCommand _mouseMoveCommand;
public RelayCommand MMCommand
{
get
{
if (_mouseMoveCommand == null) return _mouseMoveCommand = new RelayCommand(param => ExecuteMouseMove((MouseEventArgs)param));
return _mouseMoveCommand;
}
set { _mouseMoveCommand = value; }
}
private void ExecuteMouseMove(MouseEventArgs e)
{
if(IsMouseCaptured)
{
var image = (System.Windows.Controls.Image)e.OriginalSource;
var border = (IInputElement)image.Parent;
Vector v = start - e.GetPosition(border);
MouseX = origin.X - v.X;
MouseY = origin.Y - v.Y;
}
}
private RelayCommand _MLBUCommand;
public RelayCommand MLBUCommand
{
get
{
if (_MLBUCommand == null) return _MLBUCommand = new RelayCommand(param => Execute_MLBU((MouseEventArgs)param));
return _MLBUCommand;
}
set { _MLBUCommand = value; }
}
private void Execute_MLBU(MouseEventArgs param)
{
IsMouseCaptured = false;
}
Mouse Behaviors as Attached properties
public class MouseBehaviour
{
#region MouseUp
public static readonly DependencyProperty MouseUpCommandProperty =
DependencyProperty.RegisterAttached("MouseUpCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseUpCommandChanged)));
private static void MouseUpCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseUp += element_MouseUp;
}
static void element_MouseUp(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseUpCommand(element);
command.Execute(e);
}
public static void SetMouseUpCommand(UIElement element, ICommand value)
{
element.SetValue(MouseUpCommandProperty, value);
}
public static ICommand GetMouseUpCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseUpCommandProperty);
}
#endregion
#region MouseDown
public static readonly DependencyProperty MouseDownCommandProperty =
DependencyProperty.RegisterAttached("MouseDownCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseDownCommandChanged)));
private static void MouseDownCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseDown += element_MouseDown;
}
static void element_MouseDown(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseDownCommand(element);
command.Execute(e);
}
public static void SetMouseDownCommand(UIElement element, ICommand value)
{
element.SetValue(MouseDownCommandProperty, value);
}
public static ICommand GetMouseDownCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseDownCommandProperty);
}
#endregion
#region MouseMove
public static readonly DependencyProperty MouseMoveCommandProperty =
DependencyProperty.RegisterAttached("MouseMoveCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseMoveCommandChanged)));
private static void MouseMoveCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseMove += new MouseEventHandler(element_MouseMove);
}
static void element_MouseMove(object sender, MouseEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseMoveCommand(element);
command.Execute(e);
}
public static void SetMouseMoveCommand(UIElement element, ICommand value)
{
element.SetValue(MouseMoveCommandProperty, value);
}
public static ICommand GetMouseMoveCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseMoveCommandProperty);
}
#endregion
}

Unable to check all / select all items in a CheckListBox in WPF [duplicate]

I have a UserControl that I add to my main application.
That UserControl contains a button for a UIElement
The UserControl contains a DispatchTimer and every 2 seconds based on some int values determines what the button image will be.
One of the methods called in the UserControl should set it's image but the control never displays the image that it was changed to.
public void SetNormal()
{
btnFlashAlert.Content = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
}
Is there something i'm missing to get the look of the control update on the main application?
When I look at what .Content contains, it is correct. The UI doesn't reflect the change.
XAML
<UserControl x:Class="SC.FlashSystem.MainButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="53" Width="164">
<Button x:Name="btnFlashAlert" Background="{x:Null}" BorderBrush="{x:Null}" Cursor="Hand" Click="btnFlashAlert_Click">
<Button.Template>
<ControlTemplate>
<Image Source="Images/FlashButton.png"/>
</ControlTemplate>
</Button.Template>
</Button>
Codebehind Updated
public partial class MainButton : UserControl
{
private SupportConsoleWeb.MessageData messageCounts { get; set; }
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
private BitmapImage NormalImage { get; set; }
private BitmapImage CriticalImage { get; set; }
private BitmapImage AlertImage { get; set; }
private BitmapImage InfoImage { get; set; }
public MainButton()
{
InitializeComponent();
messageCounts = new SupportConsoleWeb.MessageData();
messageCounts.CriticalCount = 0;
messageCounts.AlertCount = 0;
messageCounts.InfoCount = 0;
NormalImage = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
CriticalImage = new BitmapImage(new Uri("Images/FlashButtonRed.png", UriKind.RelativeOrAbsolute));
AlertImage = new BitmapImage(new Uri("Images/FlashButtonOrange.png", UriKind.RelativeOrAbsolute));
InfoImage = new BitmapImage(new Uri("Images/FlashButtonGreen.png", UriKind.RelativeOrAbsolute));
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += flashButtonChangeTimer_Tick;
flashButtonChangeTimer.Start();
}
void flashButtonChangeTimer_Tick(object sender, EventArgs e)
{
btnFlashAlert.Dispatcher.BeginInvoke(new Action(() =>
{
if (btnFlashAlert.Content == null)
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0 && btnFlashAlert.Content.Equals(CriticalImage))
{
SetNormal();
}
else if (messageCounts.AlertCount > 0 && btnFlashAlert.Content.Equals(AlertImage))
{
SetNormal();
}
else if (messageCounts.InfoCount > 0 && btnFlashAlert.Content.Equals(InfoImage))
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0)
{
SetCritical();
}
else if (messageCounts.AlertCount > 0)
{
SetAlert();
}
else if (messageCounts.InfoCount > 0)
{
SetInfo();
}
}));
}
public void UpdateMessageCounts(SupportConsoleWeb.MessageData messageCounts)
{
this.messageCounts = messageCounts;
}
private void btnFlashAlert_Click(object sender, RoutedEventArgs e)
{
MainWindow window = new MainWindow();
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
window.ShowDialog();
}
public void SetMessageCount(int criticalCount, int alertCount, int infoCount)
{
messageCounts.CriticalCount = criticalCount;
messageCounts.AlertCount = alertCount;
messageCounts.InfoCount = infoCount;
}
private void SetNormal()
{
btnFlashAlert.Content = NormalImage;
}
private void SetCritical()
{
btnFlashAlert.Content = CriticalImage;
}
private void SetAlert()
{
btnFlashAlert.Content = AlertImage;
}
private void SetInfo()
{
btnFlashAlert.Content = InfoImage;
}
}
Change your XAML To this
<Image Source="{Binding TheImage}"/>
Add notify property changed
public partial class MainButton : UserControl, INotifyPropertyChanged
Create the OnPropertyChanged Event
void OnPropertyChanged(String prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Create a Bitmap prop and notify the prop changed event
private BitmapImage _TheImage;
public BitmapImage TheImage
{
get { return _TheImage; }
set { _TheImage = value; OnPropertyChanged("TheImage"); }
}
In your initializer
public MainButton()
{
this.DataContext = this;
InitializeComponent();
TheImage = new BitmapImage();
Now in your setting methods call
TheImage = //Your Bitmap Goes here
I know this seems excessive but you will see it is a much cleaner implementation in the long run.
I believe its an issue with picture selection logic not having a default image when none of the conditions are met...
With that said, IMHO the picture logic would be better expressed by having all images pre-loaded and their visibility initially set to hidden. Then bind the visibility of each image to a specific flag boolean on the VM. Which the timer event can simply turn on or off the boolean(s) which will ultimately show or hide images as needed.
That removes any latency due to loading and showing of images for they will be pre-loaded; also it will solve any possible future memory issues due to loading/unloading of images.
Example
The following example has a button with two images. Both image's visibility is bound to Booleans on the VM. The VM has one Boolean which the imageas work off of and a timer which changes its status every two seconds switching the images.
Xaml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Button x:Name="bStatus" Width="48" Height="48">
<StackPanel Orientation="Vertical">
<Image Source="Images\Copy-icon.png" Visibility="{Binding IsCopyOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<Image Source="Images\Recycle-icon.png"
Visibility="{Binding IsRecycleOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</Button>
VM
public class MainVM : INotifyPropertyChanged
{
private bool _bSwitch;
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
public bool IsRecycleOn
{
get { return _bSwitch; }
}
public bool IsCopyOn
{
get { return !_bSwitch; }
}
public MainVM()
{
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += (sender, args) =>
{
_bSwitch = ! _bSwitch;
OnPropertyChanged("IsCopyOn");
OnPropertyChanged("IsRecycleOn");
};
flashButtonChangeTimer.Start();
}
/// <summary>Event raised when a property changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Raises the PropertyChanged event.</summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Categories

Resources