WPF | Expander Event - wpf

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;
}
}

Related

WPF Sliderf follow cursor

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>

WPF: bind property from MainWindow.xaml cause an error

So I have this view model:
public class WiresharkFiles : INotifyPropertyChanged
{
public ObservableCollection<WiresharkFile> List { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private bool _inUse;
private int _packets;
private bool _hasItems;
public WiresharkFiles()
{
List = new ObservableCollection<WiresharkFile>();
HasItems = false;
List.CollectionChanged += List_CollectionChanged;
}
private void List_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
HasItems = List.Count > 0;
}
public bool InUse
{
get { return _inUse; }
set
{
_inUse = value;
NotifyPropertyChanged("InUse");
}
}
public int Packets
{
get { return _packets; }
set
{
_packets = value;
NotifyPropertyChanged("Packets");
}
}
public bool HasItems
{
get { return _hasItems; }
set
{
_hasItems = value;
NotifyPropertyChanged("HasItems");
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml
private WiresharkFiles caps;
public MainWindow()
{
InitializeComponent();
caps = new WiresharkFiles();
}
Window.Resources
<Window.Resources>
<Convertors:CollectionHasItemsConverter x:Key="CollectionHasItemsConverter"/>
</Window.Resources>
Converter
public class CollectionHasItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And base of my collection item (empty or not) i want to enable/disable my Button:
<Button Name="btnDeleteAll"
Click="btnDeleteAll_Click"
IsEnabled="{Binding Path=(caps.HasItems),Converter={StaticResource CollectionHasItemsConverter}}">
And i got this error:
XamlParseException: Type reference cannot find type named
'{http://schemas.microsoft.com/winfx/2006/xaml/presentation}caps'.
I don't see where you're associating your DataContext with the caps property.
Make sure you have a public property because the WPF engine isn't running from within your class and won't be able to access the private WiresharkFiles caps; variable. Try the following:
private WiresharkFiles caps;
public WiresharkFiles Files { get { return caps; } }
with a corresponding
public MainWindow()
{
InitializeComponent();
caps = new WiresharkFiles();
DataContext = Files;
}
Your XAML will then bind to Files as follows
IsEnabled="{Binding Path=HasItems}"
Update You'll need to have a look at implementing and binding to commands for the button which will make it a lot better. Look at this article for info on implementing and dealing with commands.
caps is a private variable:
private WiresharkFiles caps;
In order to bind, it would have to be a public property:
public WiresharkFiles caps {get;set;}
You would also have to set the datacontext of the window to itself. Something like:
this.DataContext = this;
or
In your window tag put:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
I don't see how this relates to your initial question but you can use dot notation in binding.
You can bind:
{Binding AnObservableCollection.Count}
And you can compare that to 0 in a datatrigger. With a button and a bound command if you want to disable it then I'd use the canexecute of icommand and return false if you have no entries or whatever your logic is.

How to determine whether MediaElement is playing?

I want to determine video is_playing on my player .
But how??
Please help me.
I am using c#>w_pf .net framework 4.5.1
I want to create a event like this video.
See Photo Demo
I want to control play/pause button like that player.
How can I control play/pause button image change while video is playing.
Please help me
i did this by having 2 buttons and binding the visibility of those buttons to a bool that you control when your buttons are clicked.
basically you must keep track of the play pause state yourself. media element wont do this for you.
in main window xaml add the following:
// Two converters used with the visibility binding
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<local:InvertableBooleanToVisibilityConverter x:Key="InvertableBooleanToVisibilityConverter"/>
</Window.Resources>
// Need to make sure we add loaded behaviour or it will crash app
<MediaElement Name="mePlayer"
LoadedBehavior="Manual"
Source="C:\Users\SamG\Downloads\ZW_20160102_064536A.avi"/>
<Button Content="Play" Name="Play" Click="Play_OnClick" Visibility="{Binding ElementName=mainWindow, Path=IsPaused, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Content="Pause" Name="Pause" Click="Pause_OnClick" Visibility="{Binding ElementName=mainWindow, Path=IsPaused, Converter={StaticResource InvertableBooleanToVisibilityConverter}, ConverterParameter=Inverted}"/>
you must also name this window make sure you put this inside
x:Name="mainWindow"
in the c# code:
public partial class MainWindow : INotifyPropertyChanged
{
private bool _isPaused;
public MainWindow()
{
InitializeComponent();
mePlayer.Play();
IsPaused = true;
}
private void Play_OnClick(object sender, RoutedEventArgs e)
{
mePlayer.Play();
IsPaused = false;
}
private void Pause_OnClick(object sender, RoutedEventArgs e)
{
mePlayer.Pause();
IsPaused = true;
}
public bool IsPaused
{
get { return _isPaused; }
set
{
_isPaused = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
then add a new class called InvertableBooleanToVisibilityConverter:
[ValueConversion(typeof(bool), typeof(Visibility))]
public class InvertableBooleanToVisibilityConverter : IValueConverter
{
enum Parameters
{
Normal, Inverted
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = (bool) value;
var direction = (Parameters) Enum.Parse(typeof (Parameters), (string) parameter);
if (direction == Parameters.Inverted)
return !boolValue ? Visibility.Visible : Visibility.Collapsed;
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
copy this code it should work for what you need

Radiobutton wpf binding

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;
}
}

How do you set the target of a label so that the access key will focus on the control inside a ContentControl?

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));
}
}

Resources