MVVM Textbox gotFocus binding to a method is not working - c#

I have a method in my view model. How I can bind this method to textbox.gotfocus property.
My XAML part is:
<TextBox Style=
"{StaticResource TextBoxHadnigPanel}"
GotFocus="{Binding GotFocusCustomerNameMethod}"
LostFocus="{Binding LostFocusCustomerNameMethod}"
x:Name="TextBoxCustomerName"
Grid.Row="0"
Grid.Column="1"
MaxLength="16"
Margin="10" />
How to bind this LostFocus and GotFocus properties?
Anyone?
Thanks in advance

You can't bind a method in WPF.
Alternative: You can use a Behavior for a TextBox with MVVM.
You need a reference to System.Windows.Interactivity to achieve this.
public class TextBoxFocusBehavior : Behavior<TextBox>
{
#region Overrides of Behavior
protected override void OnAttached()
{
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
AssociatedObject.LostFocus += AssociatedObject_LostFocus;
base.OnAttached();
}
private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
{
//TODO Your LostFocus Method here.
}
private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
{
//TODO Your GotFocus Method here.
}
#endregion
}
Xaml:
You need a reference in the xaml file:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviors="clr-namespace:YourNamespace"
<TextBox Style="{StaticResource TextBoxHadnigPanel}"
x:Name="TextBoxCustomerName"
Grid.Row="0"
Grid.Column="1"
MaxLength="16"
Margin="10">
<i:Interaction.Behaviors>
<behaviors:TextBoxFocusBehavior />
</i:Interaction.Behaviors>
</TextBox>

Related

How to define custom Controller in XAML Plot

I have 2 questions about oxyplot and since they are very simple, I put them together:
How can I define custom plot Controller in XAML? I noticed there is a Controller property in PlotView class, but there is nothing I could find in the Plot class. Am I missing something, or it's not possible at the moment?
Does maximum range property work on a DateTimeAxis? What are the units? I have tried values around hundreds or thousands, but it seems that the zoom is not limited.
My plot is defined in the XAML as follows:
<oxy:Plot>
<oxy:Plot.Axes>
<oxy:DateTimeAxis Title="Time" Position="Bottom" StringFormat="HH:mm:ss" />
<oxy:LinearAxis Title="Temperature" Position="Left" Key="Temperature" />
<oxy:LogarithmicAxis Title="Pressure" Position="Right" Key="Pressure" />
</oxy:Plot.Axes>
<oxy:Plot.Series>
<oxy:LineSeries Title="Temperature" ItemsSource="{Binding Temperatures, Mode=OneWay}" DataFieldY="Temperature" YAxisKey="Temperature" />
<oxy:LineSeries Title="Pressure" ItemsSource="{Binding Pressures, Mode=OneWay}" DataFieldY="Pressure" YAxisKey="Pressure" />
</oxy:Plot.Series>
</oxy:Plot>
Adding custom controller via XAML seems impossible, so I have used System.Windows.Interactivity.Behavior to edit plot's controller without code behind.
Behavior is defined like this:
public class CustomControllerPlotBehavior : System.Windows.Interactivity.Behavior<Plot>
{
protected override void OnAttached()
{
AssociatedObject.Loaded += OnLoaded;
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
CustomizeController(AssociatedObject.ActualController);
}
private void CustomizeController(IPlotController controller)
{
controller.UnbindAll();
// actual changes to the controller
controller.BindMouseDown(OxyMouseButton.Left, OxyModifierKeys.Shift, PlotCommands.ZoomRectangle);
controller.BindMouseDown(OxyMouseButton.Left, OxyModifierKeys.Control, PlotCommands.PanAt);
controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.SnapTrack);
controller.BindKeyDown(OxyKey.Home, PlotCommands.Reset);
controller.BindMouseWheel(PlotCommands.ZoomWheel);
controller.BindMouseWheel(OxyModifierKeys.Control, PlotCommands.ZoomWheelFine);
}
}
In XAML, the behavior can be used like this:
<oxy:Plot>
<!-- Other plot settings -->
<i:Interaction.Behaviors>
<behaviors:CustomControllerPlotBehavior />
</i:Interaction.Behaviors>
</oxy:Plot>

Tap Gesture on List View Items

I am trying to open another view after tapping on an item in the list view.
I have tried adding a TapGestureRegonizer and even adding ViewCell with grids etc. None of these seem to work. I have added a tap gesture to a label and that seemed to work but the same does not work for list view items. This seems like a simple problem for something like list view, but there doesnt seem to be a built in functionality for this.
The Xaml:
<ListView x:Name="dataList"
ItemsSource="{Binding routeLabels}"
HasUnevenRows="True"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3">
</ListView>
The code behind:
var listviewgesture = new TapGestureRecognizer();
listviewgesture.SetBinding(TapGestureRecognizer.CommandProperty,"LoadRoutePage");
dataList.GestureRecognizers.Add(listviewgesture);
The view model:
public ICommand LoadRoutePage { get; protected set; }
public DriverDashboardViewModel(INavigation navigation,MessagDatabase database)
{
this._database = database;
this.Navigation = navigation;
this.LoadNotifications = new Command(async () => await OpenNotificationsPage());
this.LoadRoutePage = new Command(async () => await OpenRoutePage());
}
public async Task OpenRoutePage()
{
await Navigation.PushAsync(new RoutePageView());
}
Just to be clear the LoadNotifications method does work in opening a page but LoadRoutePage does not. So I know there is some level of communication between the view and viewmodel.
You should not be adding a TapGestureRecognizer to a ListView. Every cell already has events that handle tapping on them and a GestureRecognizer would probably only confuse the ListView regarding what the tap should be doing. There are a few ways to go about this.
1. SelectedItem binding
Bind a SelectedItem property to the ListView and handle your method calls in the setter of that property.
<ListView x:Name="dataList" ItemsSource="{Binding routeLabels}"
HasUnevenRows="True" Grid.Row="1" Grid.Column="0"
Grid.ColumnSpan="3" SelectedItem="{Binding SelectedItem}">
</ListView>
And in your viewmodel:
string _selectedItem;
public string SelectedItem {
get {return _selectedItem; }
set
{
_selectedItem = value;
// Additional code
}
}
2. Use the built in events ItemSelected or ItemTapped
A ListView has some events you can hook up named ItemSelected and ItemTapped. These can be caught in code-behind and can handle what you're trying to achieve.
<ListView x:Name="dataList" ItemsSource="{Binding routeLabels}"
HasUnevenRows="True" Grid.Row="1" Grid.Column="0"
Grid.ColumnSpan="3" ItemSelected="Handle_ItemSelected" ItemTapped="Handle_ItemTapped">
</ListView>
3. Use event to command binding with behaviors
Since you use viewmodels you ideally don't want these events since they're handled on the UI side. There are NuGet packages out there that can translate an event to a Command that you can handle in your viewmodel. Take a look at Corcav.Behaviors for example.
4. Create a behavior of your own
I have one I use regularly which looks like this:
public class ListViewSelectedItemBehavior : Behavior<ListView>
{
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ListViewSelectedItemBehavior));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public ListView AssociatedObject { get; private set; }
protected override void OnAttachedTo(ListView bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
bindable.BindingContextChanged += OnBindingContextChanged;
bindable.ItemSelected += OnListViewItemSelected;
}
protected override void OnDetachingFrom(ListView bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
bindable.ItemSelected -= OnListViewItemSelected;
AssociatedObject = null;
}
private void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
private void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (Command == null)
return;
if (Command.CanExecute(e.SelectedItem))
Command.Execute(e.SelectedItem);
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
To add this to your ListView you simply add a behavior to it:
<ListView x:Name="dataList" ItemsSource="{Binding routeLabels}"
HasUnevenRows="True" Grid.Row="1" Grid.Column="0"
Grid.ColumnSpan="3">
<ListView.Behaviors>
<behaviors:ListViewSelectedItemBehavior Command="{Binding ItemSelectedCommand}" />
</ListView.Behaviors>
</ListView>
In this case ItemSelectedCommand is a Command object in your ViewModel.
Not sure if I understand you correctly but you are trying to get an event going when someone taps on anelement of a listview?
If so you don't need a recognizer you simply have to add ItemTapped in your XAML:
<ListView x:Name="dataList"
ItemsSource="{Binding routeLabels}"
HasUnevenRows="True"
Grid.Row="1"
Grid.Column="0"
ItemTapped="Name of event"
Grid.ColumnSpan="3">
</ListView>
This will generate an event for you ( just do double tab when creating the ItemTapped ) and here you can place your code
You're binding a command instead of an event to the "Tapped" event. Try something like this:
code behind:
var listviewgesture = new TapGestureRecognizer();
listviewgesture.Tapped += Handle_listViewItemTapped;
dataList.GestureRecognizers.Add(listviewgesture);
ViewModel:
private void Handle_listViewItemTapped(object sender, EventArgs e)
{
viewModel.OpenRoutePage();
}

Caliburn.Micro attach event handler on event of Behavior

I wrote my own Behavior to handle a swipe gesture and put it to the ItemTemplate of a ListView. If a swipe is completed, I raise an event for LeftSwipe or RightSwipe. This event should be handled by my ViewModel.
I use the syntax of Caliburn.Micro to attach handler to an event: cm:Message.Attach="[Event LeftSwipe] = [LeftSwipe($source, $eventArgs)".
This is my Behavior:
public class SwipeInteractionBehavior : DependencyObject, IBehavior
{
public DependencyObject AssociatedObject { get; private set; }
public void Attach(DependencyObject associatedObject)
{
// ...
}
public void Detach()
{
// ...
}
public event EventHandler LeftSwipe;
public event EventHandler RightSwipe;
// ...
// ...
private void OnLeftSwipe(FrameworkElement element)
{
// ...
if (LeftSwipe != null)
{
LeftSwipe(this, EventArgs.Empty);
}
}
private void OnRightSwipe(FrameworkElement element)
{
// ...
if (RightSwipe != null)
{
RightSwipe(this, EventArgs.Empty);
}
}
}
This is how I use this Behavior inside of ListViews ItemTemplate:
<ListView x:Name="Links" IsItemClickEnabled="True" SelectionMode="None" cm:Message.Attach="[Event ItemClick] = [Click($source, $eventArgs)]">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid>
<Border x:Name="todoItem" Loaded="Border_Loaded" Background="White">
<i:Interaction.Behaviors>
<local:SwipeInteractionBehavior cm:Message.Attach="[Event LeftSwipe] = [LeftSwipe($source, $eventArgs)]" />
</i:Interaction.Behaviors>
<Grid>
<StackPanel>
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemContentTextBlockStyle}" />
<TextBlock Text="{Binding Url}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
<TextBlock Text="{Binding Tags, Converter={StaticResource ListToString}}" />
</StackPanel>
</Grid>
</Border>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I ran in an exception if I raise the LeftSwipe event, this is my StackTrace:
System.Exception was not handled.
HResult=-2146233088
Message=No target found for method LeftSwipe.
Source=Caliburn.Micro.Platform
StackTrace:
at Caliburn.Micro.ActionMessage.Invoke(Object eventArgs)
at Caliburn.Micro.TriggerAction`1.Execute(Object sender, Object parameter)
at Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender, ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTriggerBehavior.OnEvent(Object sender, Object eventArgs)
at ReaderApp.SwipeInteractionBehavior.<>c__DisplayClass5.<OnLeftSwipe>b__4()
at ReaderApp.Extensions.FrameworkElementExtension.<>c__DisplayClass2.<Animate>b__0(Object s, Object e)
InnerException:
ViewModel:
public class MainViewModel : ViewModelBase
{
private readonly IEventAggregator eventAggregator;
private readonly Database database;
public BindableCollection<Link> Links
{
get;
private set;
}
public MainViewModel(INavigationService navigationService, IEventAggregator eventAggregator, Database database)
: base(navigationService)
{
this.eventAggregator = eventAggregator;
this.database = database;
Links = new BindableCollection<Link>();
}
public async void LeftSwipe(object sender, EventArgs e)
{
// ...
}
public void RightSwipe(object sender, EventArgs e)
{
// ...
}
}
So the problem is that ActionMessage inherits TriggerAction<FrameworkElement> which means it can't attach correctly to SwipeInteractionBehavior.
It's also complicated by the fact there's some major API differences between the WPF / Silverlight / Windows Phone 8 Interactivity SDK and the WinRT Interactivity SDK. You can see a bit of what I mean in the Parser comments.
What I'd recommend is implementing SwipeInteractionBehavior as a Trigger Behaviour much like EventTriggerBehavior, this used to be a separate base class but with WinRT it's still IBehavior, the difference is that it has a property called Actions of type ActionCollection. Stupidly there is no interface is base class enforcing this so Caliburn.Micro is stuck making some assumptions.
You'd then use Interaction.ExecuteActions to trigger those actions, in the end your xaml should look something like.
<Border x:Name="SwipeTarget">
<i:Interaction.Behaviors>
<local:SwipeEventBehavior Direction="Left">
<cm:ActionMessage AssociatedObject="{Binding ElementName=SwipeTarget" Method="LeftSwipe" />
</local:SwipeEventBehavior>
</i:Interaction.Behaviors>
</Border>
It's a bit more long winded, but we need to work around the limitations imposed by the changes in the SDK.

WPF c# change sender property in event

Is it possible to change sender's property in event?
I have my own control in wpf with 10 Image controls.
I set on all of them mouse enter and mouse leave events.
All those events do the same(change size and Z index) but for specific Image.
With changing sender's property in event I will have only 2 event's methods, not 20.
When I tried to change sender's property I saw it was readonly.
Is it possible to do ?
Point all your controls at the same handlers. You can do this at design time or through code.
In the handler cast sender to the type of control.
Now when you change it's properties, you are changing the properties of the control that raised the event
PS don't forget to check to see if the cast is valid, before you try to access it's members.
Here how you can do it in xaml
/ <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void image1_MouseEnter(object sender, MouseEventArgs e)
{
//put your code here and all your images will points here
}
private void image1_MouseLeave(object sender, MouseEventArgs e)
{
//put your code here and all your images will points here
}
}
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image2" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image3" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image4" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
</Grid>
</Window>
//and here in code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
image1.MouseEnter += image1_MouseEnter;
image2.MouseEnter += image1_MouseEnter;
image3.MouseEnter += image1_MouseEnter;
image4.MouseEnter += image1_MouseEnter;
image1.MouseEnter += image1_MouseLeave;
image2.MouseEnter += image1_MouseLeave;
image3.MouseEnter += image1_MouseLeave;
image4.MouseEnter += image1_MouseLeave;
}
private void image1_MouseEnter(object sender, MouseEventArgs e)
{
}
private void image1_MouseLeave(object sender, MouseEventArgs e)
{
}
}

How can I raise a custom Routed Event from user control?

In my user control I have a button that, when clicked, would raise a custom Routed Event. I've attempted to raise it, but it doesn't get fired in the MainWindow.xaml.
Xaml for the button in UserControl:
<Button x:Name="PART_Add" Content="+" Grid.Column="3" Margin="0,0,0,0" Style="{DynamicResource dTranspButton}" Click="btnAdd_Click"/>
UserControl C# code:
//AddClick Event
public static readonly RoutedEvent AddClickEvent = EventManager.RegisterRoutedEvent("AddClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(dCB_Props));
public event RoutedEventHandler AddClick
{
add { AddHandler(AddClickEvent, value); }
remove { RemoveHandler(AddClickEvent, value); }
}
void RaiseAddClickEvent()
{
RoutedEventArgs newEventArgs = new RoutedEventArgs(dCB_Props.AddClickEvent);
}
protected void OnAddClick()
{
RaiseAddClickEvent();
}
//objects events
private void btnAdd_Click(object sender, System.Windows.RoutedEventArgs e)
{
RaiseAddClickEvent();
}
Xaml Code for the UserControl Instance in MainWindow.xaml:
<local:dCB_Props x:Name="cb1" Margin="41.166,0,36.19,25" VerticalAlignment="Bottom" Height="30" Width="141" AddClick="dCB_Props_AddClick">
<local:dCB_Props.Items>
<ComboBoxItem Content="item1"/>
</local:dCB_Props.Items>
</local:dCB_Props>
C# Code that should get fired in MainWindow.xaml.cs:
private void dCB_Props_AddClick(object sender, System.Windows.RoutedEventArgs e)
{
MessageBox.Show("This Works");
}
You need to call
RaiseEvent(new RoutedEventArgs(AddClickEvent));

Categories

Resources