Reputation: 934
First: Not a duplicate of Binding Button click to a method --- it's about button, and Relay command can't pass the arguments I need
Also, not a duplicate of How do you bind a Button Command to a member method? - it's a simple method with no arguments - nothing to do with my question.
Obviously (but just to make sure and avoid trolls) not a duplicate of this either Silverlight MVVM: where did my (object sender, RoutedEventArgs e) go?.
Now after clearing this (sorry, I am just really sick of being marked as "duplicate" by people who didn't understand my question), let's talk about the issue: :D
I am trying to bind a generated slider (using data template) to an event (value changed), I know it's impossible to bind an event and I must use ICommand, but I don't know how to get the event arguments to the command function, this is the xaml relevant code: (without the binding since it doesnt work)
<Slider Grid.Column="1" Grid.Row="1" Height="30" IsSnapToTickEnabled="True" Maximum="100" SmallChange="1" IsMoveToPointEnabled="True"/>
And this is the function I want it to be binded to:
public void vibrationSlider_move(object Sender, RoutedPropertyChangedEventArgs<double> e)
{
VibrationValue = (byte)e.NewValue;
SendPacket(cockpitType, (byte)Index.VibrationSlider, VibrationValue);
}
As you can see, I need to use the 'e' coming with the event, I have no idea how to reach it without using the "ValueChanged" slider event.
Notes:
Please don't tell me to add the "ValueChanged" attribute like this:
<Slider ValueChanged="VibrationSlider_move"/>
:)
It's a generated dynamic slider using DataTemplate with an observableCollection, the function isn't in the window.cs file, therefore just using an event is not possible.
Thank you.
Upvotes: 1
Views: 1969
Reputation: 5121
You could create an extension
public partial class Extensions
{
public static readonly DependencyProperty ValueChangedCommandProperty = DependencyProperty.RegisterAttached("ValueChangedCommand", typeof(ICommand), typeof(Extensions), new UIPropertyMetadata((s, e) =>
{
var element = s as Slider;
if (element != null)
{
element.ValueChanged -= OnSingleValueChanged;
if (e.NewValue != null)
{
element.ValueChanged += OnSingleValueChanged;
}
}
}));
public static ICommand GetValueChangedCommand(UIElement element)
{
return (ICommand)element.GetValue(ValueChangedCommandProperty);
}
public static void SetValueChangedCommand(UIElement element, ICommand value)
{
element.SetValue(ValueChangedCommandProperty, value);
}
private static void OnSingleValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
var element = sender as Slider;
var command = element.GetValue(ValueChangedCommandProperty) as ICommand;
if (command != null && command.CanExecute(element))
{
command.Execute(element);
e.Handled = true;
}
}
}
which then can be used in xaml as below.
<Slider Minimum="0" Maximum="100" local:Extensions.ValueChangedCommand="{Binding ValueChangedCommand}"/>
Upvotes: 2
Reputation: 8404
As @Philip W stated, you could use e.g. MVVMLight
to help dealing with MVVM pattern and with your problem at hand.
You could, for example, have a XAML
with DataTemplate
and Slider
like so:
<Window x:Class="WpfApplication1.MainWindow"
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"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
mc:Ignorable="d"
Title="MainWindow"
Height="250"
Width="250">
<Window.Resources>
<DataTemplate x:Key="SomeTemplate">
<StackPanel Margin="15">
<!-- Wrong DataContext can drive you mad!1 -->
<StackPanel.DataContext>
<local:SomeTemplateViewModel />
</StackPanel.DataContext>
<TextBlock Text="This is some template"/>
<Slider
Height="30"
IsSnapToTickEnabled="True"
Maximum="100"
SmallChange="1"
IsMoveToPointEnabled="True">
<!-- Bind/pass event as command -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<command:EventToCommand
Command="{Binding Mode=OneWay, Path=ValueChangedCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Slider>
<!-- Show current value, just for sake of it... -->
<TextBlock
Text="{Binding Value}"
FontWeight="Bold"
FontSize="24">
</TextBlock>
</StackPanel>
</DataTemplate>
</Window.Resources>
<ContentControl ContentTemplate="{StaticResource SomeTemplate}" />
</Window>
So basically you bind desired event
to named Command
and pass EventArgs
to it as parameter. Then in your ViewModel
, being the DataContext
of you Slider, you handle the event-passed-as-command.
public class SomeTemplateViewModel : ViewModelBase
{
private double _value;
public SomeTemplateViewModel()
{
// Create command setting Value as Slider's NewValue
ValueChangedCommand = new RelayCommand<RoutedPropertyChangedEventArgs<double>>(
args => Value = args.NewValue);
}
public ICommand ValueChangedCommand { get; set; }
public double Value
{
get { return _value; }
set { _value = value; RaisePropertyChanged(); } // Notify UI
}
}
This would give you something similar to this.
Upvotes: 2
Reputation: 791
You can use the MVVMLight Toolkit, which allows to send the EventArgs as CommandParameter to the ViewModel:
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<cmd:EventToCommand Command="{Binding ValueChangedCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
In your command.Execute method, you now get an object as parameter which you just have to parse to the correct type...
Upvotes: 4
Reputation: 23088
Since your slider is dynamically generated, nothing prevents you from adding your ValueChanged event at a later time:
XAML:
<Slider x:Name="slider" HorizontalAlignment="Left" Margin="10,143,0,0" VerticalAlignment="Top" Width="474" Grid.ColumnSpan="2" />
Code-behind:
public MainWindow()
{
InitializeComponent();
// it is a good idea to not allow designer to execute custom code
if (DesignerProperties.GetIsInDesignMode(this))
return;
slider.ValueChanged += Slider_ValueChanged;
}
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
// do your stuff here
}
Checking design mode
is not simple in any context, as pointed out here.
Upvotes: 0