WPF Button with Multiple Content - wpf

I am new in WPF,I am using Button control in WPF windows application, I am displaying its content from Database,
I have its click event binded with its Name,like this
<Button Content="{Binding FirstName}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="628,178,0,0" x:Name="btn1Click"/>
This button have different content for each user.
This is my FirstName property
string _fname;
public string FirstName
{
get { return _fname; }
set { _fname= value; NotifyOfPropertyChange("FirstName"); }
}
I want to get ID of the clicked user, how can I put UserID in content(with firstname)?
or in x:Name so I can identify which user is clicked?
Or I can use some hidden field for it and how?

You can bind a property to commandParameter and can check where you have binded the command to the event handler. like
<Button Command="{Binding BtnClickCommand}" CommandParameter="{Binding Id}" />
In ViewModel of this view:
BtnClickCommand=new RelayCommand(o=>BtnClickEvent((string)o));// Cast you id to whatever data type u have.
private void BtnClickEvent(string Id)
{
//Here you got your id and do what u want..
}

Assign DataContext property of button's parent with the user object then in the click event of a button write following code.
private void Button_Click(object sender, RoutedEventArgs e)
{
Button item = sender as Button;
if(item != null)
{
//you will get user details here by using item.DataContext property
User clickedUser = item.DataContext as User;
}
}
Here you will get all the details of Clicked User.

Related

Prevent jumping to next row when user hits Enter in RadGridView

I have a RadGridView from Telerik UI for WPF with an editable column. When user edits a cell in that column and hits Enter, the same cell in next row in the grid gets focus.
I want the cell that the user just edited to lose focus. How to do this MVVM-style?
You can change this behavior by writing a custom keyboard command provider. This provider overrides the default Enter press action. It commits the edits and deselects the cellby setting the SelectedItem to null.
public class CustomKeyboardCommandProvider : DefaultKeyboardCommandProvider
{
private readonly GridViewDataControl _grid;
public CustomKeyboardCommandProvider(GridViewDataControl grid)
: base(grid)
{
_grid = grid;
}
public override IEnumerable<ICommand> ProvideCommandsForKey(Key key)
{
var commands = base.ProvideCommandsForKey(key).ToList();
if (key != Key.Enter)
return commands;
commands.Clear();
commands.Add(RadGridViewCommands.CommitEdit);
_grid.SelectedItem = null;
return commands;
}
}
You have to assign the new provider. The easiest way is in code-behind, as it has a constructor parameter.
MyRadDataGridView.KeyboardCommandProvider = new CustomKeyboardCommandProvider(TestGridView);
Alternatively, create a TriggerAction<RadGridView> and attach it to the Loaded event of the grid view.
public class SetCustomKeyboardCommandProviderAction : TriggerAction<RadGridView>
{
protected override void Invoke(object parameter)
{
AssociatedObject.KeyboardCommandProvider = new CustomKeyboardCommandProvider(AssociatedObject);
}
}
<telerik:RadGridView>
<b:Interaction.Triggers>
<b:EventTrigger EventName="Loaded">
<local:SetCustomKeyboardCommandProviderAction/>
</b:EventTrigger>
</b:Interaction.Triggers>
<!-- ...other definitions. -->
</telerik:RadGridView>

How to bind tooltip to a button for different conditions

How to bind tooltip dynamically for different conditions
we have 2 Projects in the solution v are using PRISM framework
GeneralBL contains the business logic and
StudentManagementUI contains the Usercontrols ,views and ViewModels
Have StudentStatusUserControl.xaml.cs contains a Telerik RadButton
<telerik:RadButton Name="button1" Content="Stauses" Height="24" HorizontalAlignment="Left" VerticalAlignment="Top" Width="112" FontSize="12" Margin="2,2,2,2"
prism:Click.Command="{Binding ButtonstatusCommand}">
this is enabled for a specific condition & when it is disabled we have to show the mouse hover or tooltip info depending on the condition
In the StudentStatusViewModel.cs
private bool CanExecuteButtonStatusCommand(object o)
{
return SharedLogicBL.CanExecuteButtonStatusCommand(controller,dataService, _selectedItem);
}
SharedLogicBL.cs in GeneralBL project
public static bool CanExecuteUnplannedInspection(BaseController controller, DataService dataService, SDataItem selectedItem)
{
if(controller.currentuser.Isallowed())
{
if(selectedItem!=null)
{
Orders = dataservice.GetOrders(selectedItem);
return !Orders.Any();
}
}
else
return false;
}
In the above method check to see if the user has the rights, if not Tooltip on the button "User doesn't have the rights"
Let first condition is true , in the Orders.Any() returns false then we should display "the selected student has no orders"
Also have a dependency property in the StudentStatusUserControl.xaml.cs for this StudentStatusUserControlBL in the GeneralBL project
Create a public property in your viewmodel that you can databind the telerik button tooltip text to.
public string Button1TooltipText
{
get {
if (!controller.currentuser.Isallowed())
{ return "User doesn't have the rights" }
else
{
if (!SharedLogicBL.CanExecuteButtonStatusCommand(controller, dataService, _selectedItem))
return "the selected student has no orders";
else
return "Execute the unplanned inspection";
}
}
}
Since this property depends on the currently selected item, you'll need to call NotifyPropertyChanged("Button1TooltipText") when _selectedItem changes.

How to automatically select a WPF ListViewItem

I have a ListView in my WPF UserControl using an ItemTemplate to display the items. Within the template is a button. When I select one item and then click on the button of another item, the previously selected item is still selected. I wonder how to automatically select the item the button is in when the button is clicked.
Xaml
<UserControl.Resources>
<DataTemplate x:Key="ItemTemplate">
<Border>
<Grid>
<!-- lots of stuff go here -->
<Button Click="MyButton_Click">Clickme</Button>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
<ListView x:Name="_listView"
ItemTemplate="{StaticResource ItemTemplate}">
</ListView>
C# Code behind
void MyButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show( string.Format( "clicked on {0}",
this._listView.SelectedItem.ToString() ) ) ;
}
I would do it by getting the data context of the sender object. Assuming your listview is a list of objects of type MyObject... then something like this would allow you to reference the selected object.
void MyButton_Click(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
if (b == null)
{
return;
}
MyObject o = b.DataContext as MyObject;
if (o != null)
{
// Put stuff for my object here
}
}
When you press the button your click / mouse down event is handled by the button and therefore does not route through to the ListView control.
A possible way to solve this is to manually set the listview.SelectedItem in the button click event.

Passing origin of ContextMenu into WPF Command

Interesting problem related to firing commands from context menu items...
I want to fire a command to insert a row in my control, InsertRowCmd. This command needs to know where to insert the row.
I could use Mouse.GetPosition(), but that would get me the position of the mouse currently, which would be over the menu item. I want to get the origin of the context menu instead.
Does any one have any suggestions on how to pass the origin of the context menu as a parameter to the command?
Sample code:
<UserControl x:Name="MyControl">
<!--...-->
<ContextMenu x:Name="menu">
<MenuItem Header="Insert Row" Command="{x:Static customCommands:MyCommands.InsertRowCmd}" CommandParameter="?"/>
</ContextMenu>
</UserControl>
My current ideas are as follows:
-Use click handler instead so that I can find the origin in code. The problem is that I would then have to handle enabling/disabling.
-Handle click event and save the origin of the context menu. Pass this saved information into the command. I have verified that click events fire before the command is executed.
Any ideas?
EDIT:
I'm using Josh Smith's CommandSinkBinding to route the command handling into my ViewModel class. So the code that handles the command execution knows nothing about the view.
You'll need to use TranslatePoint to translate the top-left (0, 0) of the ContextMenu to a coordinate in the containing grid. You could do so by binding the CommandParameter to the ContextMenu and use a converter:
CommandParameter="{Binding IsOpen, ElementName=_menu, Converter={StaticResource PointConverter}}"
Another approach would be an attached behavior that automatically updates an attached readonly property of type Point whenever the ContextMenu is opened. Usage would look something like this:
<ContextMenu x:Name="_menu" local:TrackBehavior.TrackOpenLocation="True">
<MenuItem Command="..." CommandParameter="{Binding Path=(local:TrackBehavior.OpenLocation), ElementName=_menu}"/>
</ContextMenu>
So the TrackOpenLocation attached property does the work of attaching to the ContextMenu and updating a second attached property (OpenLocation) whenever the ContextMenu is opened. Then the MenuItem can just bind to OpenLocation to get the location at which the ContextMenu was last opened.
Following on from Kent's answer, I used his attached property suggestion and ended up with this (using Josh Smith's example for attached behaviors):
public static class TrackBehavior
{
public static readonly DependencyProperty TrackOpenLocationProperty = DependencyProperty.RegisterAttached("TrackOpenLocation", typeof(bool), typeof(TrackBehavior), new UIPropertyMetadata(false, OnTrackOpenLocationChanged));
public static bool GetTrackOpenLocation(ContextMenu item)
{
return (bool)item.GetValue(TrackOpenLocationProperty);
}
public static void SetTrackOpenLocation(ContextMenu item, bool value)
{
item.SetValue(TrackOpenLocationProperty, value);
}
public static readonly DependencyProperty OpenLocationProperty = DependencyProperty.RegisterAttached("OpenLocation", typeof(Point), typeof(TrackBehavior), new UIPropertyMetadata(new Point()));
public static Point GetOpenLocation(ContextMenu item)
{
return (Point)item.GetValue(OpenLocationProperty);
}
public static void SetOpenLocation(ContextMenu item, Point value)
{
item.SetValue(OpenLocationProperty, value);
}
static void OnTrackOpenLocationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var menu = dependencyObject as ContextMenu;
if (menu == null)
{
return;
}
if (!(e.NewValue is bool))
{
return;
}
if ((bool)e.NewValue)
{
menu.Opened += menu_Opened;
}
else
{
menu.Opened -= menu_Opened;
}
}
static void menu_Opened(object sender, RoutedEventArgs e)
{
if (!ReferenceEquals(sender, e.OriginalSource))
{
return;
}
var menu = e.OriginalSource as ContextMenu;
if (menu != null)
{
SetOpenLocation(menu, Mouse.GetPosition(menu.PlacementTarget));
}
}
}
and then to use in the Xaml, you just need:
<ContextMenu x:Name="menu" Common:TrackBehavior.TrackOpenLocation="True">
<MenuItem Command="{Binding SomeCommand}" CommandParameter="{Binding Path=(Common:TrackBehavior.OpenLocation), ElementName=menu}" Header="Menu Text"/>
</ContextMenu>
However, I also needed to add:
NameScope.SetNameScope(menu, NameScope.GetNameScope(this));
to the constructor of my view, otherwise the binding for the CommandParameter couldn't lookup ElementName=menu.
In addition to Kent's answer, think about a "standard way". F.e. when a ListBox has a ContextMenu, you do not need menu's position, because the selected item is set before the menu popped up. So, if your control would have something that gets "selected" on the right click...

ComboBox with ItemTemplate that includes a button

So, lets say I have a ComboBox with a custom data template. One of the items in the data template is a button:
<ComboBox Width="150" ItemsSource="{Binding MyItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Button Content="ClickMe" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The problem with this is that the button eats the click, and the item does not get selected if the button is selected. This means that the pull-down does not go away, and no item is selected.
I get WHY this is happening.
Is there a way to work around it? Possibly a way to process the button click (I am binding to a command) and tell it to continue up the chain so the combo box can also process the click?
Note: I am seeing my problem in Silverlight, but I am guessing that the exact same behavior can be seen with WPF.
OK, I got it figured out. It is a total hack, but it still lets me bind my command to the button and continue to have Combo-box behavior for selecting the item:
<ComboBox x:Name="MyCombo" Width="150" ItemsSource="{Binding MyItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Button Content="ClickMe" Click="Button_Click" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
And in the code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
MyCombo.SelectedItem = (sender as Button).DataContext;
MyCombo.IsDropDownOpen = false;
}
If I really wanted to, I could bind the SelectedItem and IsDropDownOpen to properties in my ViewModel but I decided against it to keep this behavior as a hack extension of the XAML, in an effort to keep my ViewModel clean.
Your best bet would probably be to set the SelectedItem in the button's command.
I found another possibility for the MVVM context. I used an derived class for ComboBox and if an item is adden which derives from ButtonBase I attach to the Click event to close the ComboBox.
This works for my project - but just, because the items itself are buttons, it would not work if they just contain buttons as a child element.
public class MyComboBox : ComboBox
{
public MyComboBox()
{
// use Loaded event to modify inital items.
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
if (Items != null)
{
foreach (var item in Items)
{
var button = item as ButtonBase;
if (button != null)
{
ModifyButtonItem(button);
}
}
}
}
protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
// Check added items. If an item is a button, modify the button.
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
var button = item as ButtonBase;
if (button != null)
{
ModifyButtonItem(button);
}
}
}
}
private void ModifyButtonItem(ButtonBase button)
{
button.Click += (sender, args) => { IsDropDownOpen = false; };
}
}
I don't know if there is a way to do what you want. If you were to put a Button in a ListBox, for example, the same behavior occurs - clicking the Button does not cause its item in the ListBox to be selected. In fact, this is the case for any control in an ItemsControl that supports selection.
You might be able to do something with the Click event and mark it as not handled so that it continues up the visual tree, but even then I'm not sure if that would work or not.

Resources