I have slider :
<Slider x:Name="DesktopAudioSlider"
HorizontalAlignment="Stretch"
Margin="0,0,5,0">
I need behavior like in UWP Slider , that follow the cursor when it click. I cant use events because use MVVM , tried to convert sender and eventargs to tuple and pass to command , but there is no way how to pass MouseEventargs to converter.
Converter :
class SliderArgsToTupleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(parameter is MouseEventArgs)
{
return new Tuple<object, MouseEventArgs>(value, parameter as MouseEventArgs);
}
else
{
throw new ArgumentException();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Command :
this.SlideMouseMoveCommand = new RelayCommand<Tuple<object, MouseEventArgs>>(SlideMouseMoveExecute);
Trigger :
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{Binding MixerViewModel.SlideMouseMoveCommand, Source={StaticResource Locator}}"
CommandParameter="{Binding ElementName=DesktopAudioSlider , ConverterParameter={}}"
</i:EventTrigger>
There is no way how to pass two arguments in command , i found the one solution - add behavior.
public class SliderMoveBehavior : Behavior<Slider>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseMove += SliderMouseMove;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseMove -= SliderMouseMove;
}
private void SliderMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point position = e.GetPosition(AssociatedObject);
double d = 1.0d / AssociatedObject.ActualWidth * position.X;
var p = AssociatedObject.Maximum * d;
AssociatedObject.Value = (int)p;
}
}
}
In XAML :
<Slider x:Name="PlaybackSpeedSlider">
<i:Interaction.Behaviors>
<behaviors:SliderMoveBehavior/>
</i:Interaction.Behaviors>
</Slider>
Related
I want all characters in a TextBlock to be displayed in uppercase
<TextBlock Name="tbAbc"
FontSize="12"
TextAlignment="Center"
Text="Channel Name"
Foreground="{DynamicResource {x:Static r:RibbonSkinResources.RibbonGroupLabelFontColorBrushKey}}" />
The strings are taken through Binding. I don't want to make the strings uppercase in the dictionary itself.
Or use
Typography.Capitals="AllSmallCaps"
in your TextBlock definition.
See here: MSDN - Typography.Capitals
EDIT:
This does not work in Windows Phone 8.1, only in Windows 8.1 ...
Implement a custom converter.
using System.Globalization;
using System.Windows.Data;
// ...
public class StringToUpperConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value is string )
{
return ((string)value).ToUpper();
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then include that in your XAML as a resource:
<local:StringToUpperConverter x:Key="StringToUpperConverter"/>
And add it to your binding:
Converter={StaticResource StringToUpperConverter}
You can use an attached property like this:
public static class TextBlock
{
public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.RegisterAttached(
"CharacterCasing",
typeof(CharacterCasing),
typeof(TextBlock),
new FrameworkPropertyMetadata(
CharacterCasing.Normal,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.NotDataBindable,
OnCharacterCasingChanged));
private static readonly DependencyProperty TextProxyProperty = DependencyProperty.RegisterAttached(
"TextProxy",
typeof(string),
typeof(TextBlock),
new PropertyMetadata(default(string), OnTextProxyChanged));
private static readonly PropertyPath TextPropertyPath = new PropertyPath("Text");
public static void SetCharacterCasing(DependencyObject element, CharacterCasing value)
{
element.SetValue(CharacterCasingProperty, value);
}
public static CharacterCasing GetCharacterCasing(DependencyObject element)
{
return (CharacterCasing)element.GetValue(CharacterCasingProperty);
}
private static void OnCharacterCasingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Controls.TextBlock textBlock)
{
if (BindingOperations.GetBinding(textBlock, TextProxyProperty) == null)
{
BindingOperations.SetBinding(
textBlock,
TextProxyProperty,
new Binding
{
Path = TextPropertyPath,
RelativeSource = RelativeSource.Self,
Mode = BindingMode.OneWay,
});
}
}
}
private static void OnTextProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(System.Windows.Controls.TextBlock.TextProperty, Format((string)e.NewValue, GetCharacterCasing(d)));
string Format(string text, CharacterCasing casing)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
switch (casing)
{
case CharacterCasing.Normal:
return text;
case CharacterCasing.Lower:
return text.ToLower();
case CharacterCasing.Upper:
return text.ToUpper();
default:
throw new ArgumentOutOfRangeException(nameof(casing), casing, null);
}
}
}
}
Then usage in xaml will look like:
<StackPanel>
<TextBox x:Name="TextBox" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="{Binding ElementName=TextBox, Path=Text}" />
<Button local:TextBlock.CharacterCasing="Upper" Content="abc" />
<Button local:TextBlock.CharacterCasing="Upper" Content="{Binding ElementName=TextBox, Path=Text}" />
</StackPanel>
If it's not a big deal you could use TextBox instead of TextBlock like this:
<TextBox CharacterCasing="Upper" IsReadOnly="True" />
While there's already a great answer here that uses a converter, I'm providing an alternative implementation that simplifies the conversion to a single line (thanks to null coalescing), as well as making it a subclass of MarkupExtension so it's easier to use in XAML.
Here's the converter...
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace IntuoSoft.Wpf.Converters {
[ValueConversion(typeof(string), typeof(string))]
public class CapitalizationConverter : MarkupExtension, IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> (value as string)?.ToUpper() ?? value; // If it's a string, call ToUpper(), otherwise, pass it through as-is.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}
And here's how you use it (Note: This assumes the above namespace is prefixed with is in your XAML):
<TextBlock Text={Binding SomeValue, Converter={is:CapitalizationConverter}}" />
Because it's a MarkupExtension subclass, you can simply use it right where/when it's needed. No need to define it in the resources first.
I use a character casing value converter:
class CharacterCasingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = value as string;
if (s == null)
return value;
CharacterCasing casing;
if (!Enum.TryParse(parameter as string, out casing))
casing = CharacterCasing.Upper;
switch (casing)
{
case CharacterCasing.Lower:
return s.ToLower(culture);
case CharacterCasing.Upper:
return s.ToUpper(culture);
default:
return s;
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I'd like to make a slider like this:
Notice there are little "|" between each slider values, and the user can only select those value.
But I found it is no use to set the SmallChange and LargeChange.
<TextBlock FontSize="44" Text="{Binding ElementName=ColorDarknessStepsSlider, Path=Value}"/>
<Slider x:Name="ColorDarknessStepsSlider" Margin="-12,0" Minimum="3" Maximum="5"
SmallChange="1" LargeChange="1" />
I expect the user could only select 3, 4, 5 but It give me this:
I don't know what's the problem. I am OK with no "|" mark between values, but at least the user must only select the integer value 3, 4, 5. How can I do this?
You can do that with xaml [EDIT : Exist only in WPF, appologies] :
<Slider x:Name="ColorDarknessStepsSlider" Margin="-12,0" Minimum="3" Maximum="5"
SmallChange="1" LargeChange="1" TickFrequency="1" />
Or via C# [EDIT : a good way]
private void ColorDarknessStepsSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
double roundedValue = Math.Round(ColorDarknessStepsSlider.Value, 0);
//Check if it is a rounded value
if(ColorDarknessStepsSlider.Value != roundedValue )
ColorDarknessStepsSlider.Value = roundedValue ;
}
Hope it can help ;)
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
int step = 40;
(sender as Slider).Value = (e.NewValue % step != 0) ? (step - e.NewValue % step) + e.NewValue : e.NewValue;
}
and xaml
<Slider Minimum="400" Maximum="2000" ValueChanged="Slider_ValueChanged"/>
Hope it help;)
For step length, use Minimum, Maximum, SmallChange
For those "ticks", try to play with TickFrequency and TickPlacement
Try using a converter - that's how I did it.
xaml change is
<Slider x:Name="durationSlider" Margin="-12,0" Minimum="1" Maximum="12"
SmallChange="1" Value="{Binding Settings.Duration, Mode=TwoWay, Converter={StaticResource DoubleToIntConverter}}"/>
public class DoubleToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int rval = (int)value;
if (value != null)
{
return rval;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double val = (double)value;
return (int)val;
}
}
The binded field
private int _duration;
public int Duration
{
get
{
return _duration;
}
set
{
if (value != _duration)
{
_duration = value;
}
// Do this everytime because the initial input is a float converted down to an int
NotifyPropertyChanged("Duration");
}
}
I have a master-detail wpf application. The "master" is a datagrid , and "detail" is two radio buttons. Based on the row selection the radio buttons are checked in the "detail" section.
I am binding my Radio button the following way using a inttoboolean converter.
xaml :
<StackPanel Margin="2">
<RadioButton Margin="0,0,0,5" Content="In Detail" IsChecked="{Binding Path=itemselect.OutputType, Converter ={StaticResource radtointOTSB}, ConverterParameter= 0}"/>
<RadioButton Content="In Breif" IsChecked="{Binding Path=itemselect.OutputType, Converter ={StaticResource radtointOTSB}, ConverterParameter= 1}"/>
</StackPanel>
In the View Model:
public class radtointOTSB : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int OTint = Convert.ToInt32(value);
if (OTint == int.Parse(parameter.ToString()))
return true;
else
return false;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return parameter;
}
}
My implementation works well for the first few selections in datagrid. And all of a sudden , neither of my radio button is selected.
I have no clue on why it happens, any suggestion is welcomed.
Thanks in advance.
Search for problems with Binding multiple RadioButtons - there are enough complaints out there. Basically the binding won't receive the value of False because it not being passed to the Dependency Property..etc etc
Try using the following class instead of the regular RadioButton, bind to IsCheckedExt, as it forces the checkbox's IsChecked value to update.
public class RadioButtonExtended : RadioButton
{
public static readonly DependencyProperty IsCheckedExtProperty =
DependencyProperty.Register("IsCheckedExt", typeof(bool?), typeof(RadioButtonExtended),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsCheckedRealChanged));
private static bool _isChanging;
public RadioButtonExtended ()
{
Checked += RadioButtonExtendedChecked;
Unchecked += RadioButtonExtendedUnchecked;
}
public bool? IsCheckedExt
{
get { return (bool?)GetValue(IsCheckedExtProperty); }
set { SetValue(IsCheckedExtProperty, value); }
}
public static void IsCheckedRealChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
_isChanging = true;
((RadioButtonExtended)d).IsChecked = (bool)e.NewValue;
_isChanging = false;
}
private void RadioButtonExtendedChecked(object sender, RoutedEventArgs e)
{
if (!_isChanging)
IsCheckedExt = true;
}
private void RadioButtonExtendedUnchecked(object sender, RoutedEventArgs e)
{
if (!_isChanging)
IsCheckedExt = false;
}
}
In my console I'm using two Expanders say(x & y)
by default it has been set 'IsExpanded = False'
once I click x then y should not be Expanded vice-versa
so please reply me with a code
Even if it is possible doing it in plain markup, it would probably better to use an attached behavior. Here an example:
class Toggle
{
Expander target;
public Toggle(Expander src,Expander target)
{
this.target = target;
src.Collapsed += new RoutedEventHandler(src_Collapsed);
src.Expanded += new RoutedEventHandler(src_Expanded);
}
void src_Expanded(object sender, RoutedEventArgs e)
{
target.IsExpanded = false;
}
void src_Collapsed(object sender, RoutedEventArgs e)
{
target.IsExpanded = true;
}
}
public static class ToggleExpanderBehavior
{
static List<Toggle> toggler = new List<Toggle>();
public static Expander GetTargetExpander(DependencyObject obj)
{
return (Expander)obj.GetValue(TargetExpanderProperty);
}
public static void SetTargetExpander(DependencyObject obj, string value)
{
obj.SetValue(TargetExpanderProperty, value);
}
// Using a DependencyProperty as the backing store for TargetName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TargetExpanderProperty =
DependencyProperty.RegisterAttached("TargetExpander", typeof(Expander), typeof(ToggleExpanderBehavior), new UIPropertyMetadata(null,new PropertyChangedCallback(OnTargetChanged)));
static void OnTargetChanged(DependencyObject depo, DependencyPropertyChangedEventArgs depa)
{
if (depa.NewValue!=null)
{
if (depo is Expander)
{
var exp = depo as Expander;
toggler.Add(new Toggle(exp,depa.NewValue as Expander));
}
}
}
}
You van attach this behavior in xaml like this:
<Expander x:Name="X" IsExpanded="True" local:ToggleExpanderBehavior.TargetExpander="{Binding ElementName=Y}" >
<TextBlock>Content 1</TextBlock>
</Expander>
<Expander x:Name="Y" IsExpanded="False" local:ToggleExpanderBehavior.TargetExpander="{Binding ElementName=X}">
<TextBlock>Content 2</TextBlock>
</Expander>
</StackPanel>
and you have the toggle on.
This may help: Just register the expanded event for all your expander items (all the same event). If you open one, all others are closed:
private void expander_Expanded(object sender, RoutedEventArgs e)
{
SPExpander.Children.OfType<Expander>()
.All<Expander>(expander =>
{
if ((Expander)sender != expander)
expander.IsExpanded = false;
else
expander.IsExpanded = true;
return true;
});
}
Hope this solve you problem...
I would just Bind the IsExpanded property of one expander to the IsExpanded property on the other, then you just need to supply a value converter that inverts the value
public class BoolInverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var b = value as bool?;
return !b;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var b = value as bool?;
return !b;
}
}
In WPF, how do you set the target of a label so that the access key will set focus on the control inside a ContentControl?
I am using MVVM and so I do not want to add any code to the code behind in order to solve this.
I have already tried setting the path to "Content" and at runtime an exception was thrown because there is no converter for the data type which is set to the content of the ContentControl. If I don't set the path, then focus is set to the ContentControl itself.
<Label Target="{Binding ElementName=_myContentControl, Path=Content}"/>
Use GotFocus event.
<Label Target="myContentControl" >_Content</Label>
<ContentControl x:Name="myContentControl" GotFocus="myContentControl_GotFocus">
private void myContentControl_GotFocus(object sender, RoutedEventArgs e)
{
var cc = sender as ContentControl;
if (cc != null && cc.Content is UIElement)
((UIElement)cc.Content).Focus();
}
Another solution using the separated class FocusBehavior:
class FocusBehaviour : Behavior<ContentControl>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.GotFocus += new System.Windows.RoutedEventHandler(AssociatedObject_GotFocus);
}
void AssociatedObject_GotFocus(object sender, System.Windows.RoutedEventArgs e)
{
var c = this.AssociatedObject.Content as UIElement;
if (c != null)
c.Focus();
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.GotFocus -= new System.Windows.RoutedEventHandler(AssociatedObject_GotFocus);
}
}
XAML:
<ContentControl x:Name="myContentControl">
<i:Interaction.Behaviors>
<local:FocusBehaviour />
</i:Interaction.Behaviors>
</ContentControl>
This way requires a dll that is called System.Windows.Interactivity and is installed with Expression Blend SDK.
You could also use a converter to bind the label's Target to the Content of the ContentControl:
[ValueConversion(typeof(ContentControl), typeof(UIElement))]
public class ToContentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var source = value as ContentControl;
if (source == null)
{
throw new ArgumentException("ToContentConverter source must be a ContentControl.");
}
return source.Content;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Declare the converter:
<Converters:ToContentConverter x:Key="ToContentConverter" />
And use it:
<Label Content="_Thing:" Target="{Binding ElementName=TheContentControl, Converter={StaticResource ToContentConverter}}" />
<ContentControl Name="TheContentControl" />
The approach I went with was similar to vorrtex's idea but doesn't require adding a reference to System.Windows.Interactivity
You create a boolean attached property with an event handler for when it changes. Add this property to your content control in the xaml. When the property is added, the event handler fires and here you can subscribe to the got focus event on your content control.
In the got focus event handler, you move focus to the next object which will be the content! Be sure that you set IsTabStop=False on the content control or you won't be able to Shift+Tab out of the content.
public static bool? GetFocusContent(DependencyObject obj)
{
return (bool?)obj.GetValue(FocusContentProperty);
}
public static void SetFocusContent(DependencyObject obj, bool? value)
{
obj.SetValue(FocusContentProperty, value);
}
public static readonly DependencyProperty FocusContentProperty =
DependencyProperty.RegisterAttached("FocusContent", typeof(bool?), typeof(MyClassName),
new UIPropertyMetadata(OnFocusContentChanged));
static void OnFocusContentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (((bool?)e.NewValue).Value == true)
{
ContentControl cControl = obj as ContentControl;
if (cControl!= null)
{
cControl.GotFocus += OnGotFocus;
}
}
}
static void OnGotFocus(object sender, RoutedEventArgs e)
{
ContentControl cControl = sender as ContentControl;
// You should check the original source against the sender to make sure that
// you don't prevent focus from getting to a child of your content.
if (cControl != null && e.OriginalSource == sender)
{
cControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}