How to differ click input between inside and outside of a UserControl? - c#

(Please take each of controls stated below as control created using MVVM pattern)
So, I have a UserControl which I place on my MainWindow. I want my UserControl, if clicked (in the MainWindow, inside the UserControl), the background changed into another color, and if I click in the MainWindow, but outside of UserControl, then the UserControl's background will change to the original color.
What I've tried :
I've tried to apply a Command inside the UserControl.InputBindings which to detect Mouse Input (MouseBinding), but the only MouseBinding raised is the MouseBinding in the Window.InputBindings (which should be raised ONLY when the click input is outside the UserControl), but apparently, wherever a click happen, the only MouseBinding raised is only the one in Window.InputBindings.
Differ the CommandParameter between MouseBinding in Window.InputBindings and UserControl.InputBindings.
Question :
How to differ the MouseBinding between clicking inside the UserControl and outside?
Thanks

The solution is simple. Just attach a PreviewMouseDown event handler to both the Window and the UserControl and handle both events in the Window:
<Window ... PreviewMouseDown="Window_PreviewMouseDown">
<UserControl Name="Control" PreviewMouseDown="UserControl_PreviewMouseDown" ... / >
</Window>
...
private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Control.Background = someNewColourBrush;
}
private void UserControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Control.Background = originalColourBrush;
}

Related

WPF ToolBarControl in multiple UserControls

I'm working on a project where the request is to implement ToolBarControl in multiple UserControls.
The UserControl would mostly have that toolbar and GridView (Devexpress).
I'm using WPF with MVVM and Caliburn.Micro framework for development.
The problem is that, that I need to c/p code of the ToolBarControl in the XAML and then in ViewModel to implement the properties.
I'm searching for a better way, and for now I guess it would be reflection.
Any advice would be helpful, code examples too.
Update #2
The controls who would be in the custom toolbar, would should be able to move selected row up or down, delete item, edit and create (last two should open a new window).
Let's say I have CustomersListViewModel, who in it's CustomersListView has the custom ToolBarControl and GridControl.
When I click add button, it should open me CustomersEditViewModel.
When I click delete, it should delete selected item in list.
When I click move up, it should move up selected row.
You could use a datatemplate toolbarviemodel and toolbarview in your app.xaml and then use a contentcontrol to show the toolbar binding it to an instance of your toolbarviewmodel
app.xaml:
<ResourceDictionary>
<DataTemplate DataType="{x:Type ViewModelToolBar}">
<startViews:ViewToolBar />
</DataTemplate>
</ResourceDictionary>
and in your usercontrol:
<ContentControl Content="{Binding MyViewModelToolBar}"/>
and to execute your commands you could use a notify event with a tag or so as parameter to tell your usercontrol viewmodel wich operation should be performed.Means you bind your toolbar buttons to the notifycommand and use button name or tag as parameter.
ViewModelToolBar:
public event EventHandler Notify;
private void OnNotify(object sender)
{
Notify?.Invoke(sender, new EventArgs());
}
public ICommand NotifyCommand => new DelegateCommand<object>(OnNotify);
and in your usercontrol ViewModel:
MyViewModelToolBar = new ViewModelToolBar();
ViewModelToolBar.Notify += ViewModelToolBar_Notify;
private void ViewModelToolBar_Notify(object sender, EventArgs e)
{
switch (sender.ToString())
{
case "Case1":
"perform your operation"
break;
case "Case2":
...
break;
case "Case3":
...
break;
}
}

Calling a method from a user control to a routed event in wpf?

Can anyone help me on this problem
I have a wpf application with one Window and one User Controls.
User Control: InventoryMenu
Window: MenuView
I created a routed event on my InventoryMenu:
NewImage.AddHandler(Image.MouseDownEvent, new RoutedEventHandler(First_Click1));
Now on my First_Click1 method, I am calling on a method from MenuView
private void First_Click(object sender, RoutedEventArgs e)
{
MenuView menu = new MenuView();
menu.showInventoryView();
}
Now from MenuView:
public void showInventoryView()
{
Inventory inventoryView = new Inventory();
ChildView.Children.Clear();
ChildView.Children.Add(inventoryView);
MessageBox.Show("I was called");
}
The problem is that the method was called but the view was not shown?
Thanks!
You may want to set invertoryView.Visible = true; or inventoryView.SetVisible(true);
There is no Children Property for Window. Window is a ContentControl and has only
one Child.
You can do it in xaml very easy. Here is some example how you can do it :
<Window x:Class="TestNamespace.MenuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestNamespace"
Title="MenuView" Height="300" Width="300">
<local:InventoryMenu/>
</Window>
Where local is your Local Namespace where your UserControl is placed. And now the child of the Window is your UserControl. If you want more than one UserControl in the Window you can say that the Child of Window is a Panel like Grid or StackPanel and place your UserControls in the Panels.

Change children of StackPanel when StackPanel parent is resizing

I have some StackPanels in a Grid. They are filled with Labels (height of all labels > displayable space). A possible XAML would be:
<Grid>
<StackPanel>
<Label Content="bla" Background="lime" />
<Label ...>
...
</StackPanel>
</Grid>
Every time the size of the Grid changes, I need to change the inner content of the StackPanel. I need to hide the overflowing Label that is only shown partly. To achieve this, I can use following solutions: with a Converter and make a new class that is inherited from StackPanel.
I want to create a different way by using an attached property. I have following code:
//DepProp OverflowVisibility (double), can save height value
public static void Initialized(DependencyObject pObject, DependencyPropertyChangedEventArgs e)
{
var panel = (pObject as Panel) //the StackPanel in this case
panel.SizeChanged += panel_updateInnerLayout;
}
static void panel_LayoutUpdated(object sender, EventArgs e)
{
var parent = sender as Panel;
if(parent != null)
foreach(FrameworkElement element in parent.Children)
{
var elementPos = element.TransformToAncestor(parent).Transform(new Point(0,0));
if(element.ActualHeight + elementPos.Y >
(double)parent.GetValue(OverflowVisibilityProperty))
element.Visibility = Visibility.Hidden;
else
element.Visibility = Visibility.Visible;
}
}
And an example for usage in XAML:
<Grid>
<ItemsControl>
<ItemsPanel>
<ItemsPanelTemplate>
<StackPanel own:OverflowVisibility.OverflowVisibility="{Binding Grid height}" />
</ItemsPanelTemplate>
</ItemsPanel>
</ItemsControl>
</Grid>
Every time the StackPanel changes it's size, I can update my labels with the panel_updateInnerLayout event handler. When the size of StackPanel is changed, everything is working fine.
My Problem: The StackPanel itself doesn't raise SizeChanged because it has a bigger height than the Grid. I need an event that raises every time the Grid changes its height.
My Question: Is there any event instead of SizeChanged, that is called every time I change the Grid size? If not, is there an alternative way with an attached property to solve my problem?
I also tried to set a binding of the height of the StackPanel to ItemsControl ActualHeight but it does not raise the SizeChanged.
Unfortunately, you have chosen the wrong Panel control for your requirements. The StackPanel should only be used when the resizing of its contents is not required, because it doesn't provide any child control resizing abilities. Therefore, you should use a Grid, or any other Panel that provides resizing abilities. Please refer to the Panels Overview page on MSDN for further help with choosing the appropriate Panel.
There are 2 possible events: SizeChanged and LayoutUpdated. SizeChanged doesn't work because the StackPanel is not resized. LayoutUpdated does not work, because the sender of this event is always null and i can not locate what StackPanel was the source.
For more see: Layout Events - SizeChanged and LayoutUpdated.
So there are no possible event available for my approach.
While I searched for a solution, I also found out, that I used the term attached property also for attached behavior and blend behavior. The difference is described here. Summary: properties do nothing with the object but their presence can be used, behaviours change the object behavior. Blend behaviors are behaviors that Microsoft created for Microsoft Blend. The code in my question is attached behavior.
For attached behaviors I didn't found a solution, but for Blend behaviors i found some. You need to add a reference Micosoft.Windows.Interactivity.
After that i used this code:
public class OverflowVisibilityBehavior : Behavior<VirtualizingStackPanel>
{
protected override void OnAttached()
{
AssociatedObject.LayoutUpdated += AssociatedObject_LayoutUpdated;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.LayoutUpdated -= AssociatedObject_LayoutUpdated;
}
void AssociatedObject_LayoutUpdated(object sender, EventArgs e)
{
var parent = AssociatedObject; //that solves the problem: you can get a "sender" information
//...
// Instead of property GetValue
(VisualTreeHelper.GetParent(parent) as ItemsPresenter).ActualHeight
}
}
Some problems left: The parent of the StackPanel must raise SizeChanged. If you put a StackPanel in a StackPanel in a Grid, you need modify it.
Second: LayoutUpdated changes the StackPanel several times. More times than i need.

How to refresh custom user control in WPF from code behind or by binding

I have a TabControl and users controls are in TabItem content like that:
\\...
<TabItem.Content>
<vm:myUserControl />
</TabItem.Content>
\\...
<TabItem.Content>
<vm:otherUserControl/>
</TabItem.Content>
How can I update myUserControl when I change data in otherUserControl (example add element in list who must be showed in myUserControl) . This controls have different datacontext (from different viem model class, who inherits BaseViewModel. who impelments INotifyPropertyChanged). Data is provided by WCF client service. Thank you for help.
you can use a Mediator/Messenger or EventAggregator. so your otherUsercontrol raise the message/event and your myUserControl subscribe and react to this message/event.
or if you dont want loose coupling you could of course couple your two viewmodels direct and use some event.
There are a number of ways to achieve this. One way would be to fire an event in otherUserControl and subscribe to that event in your MainWindow and allow your MainWindow to update myUserControl.
MyUserControl XAML
<TextBlock x:Name="TextValue">Initial Text</TextBlock>
OtherUserControl XAML
<Button Click="Button_Click">Click Me</Button>
OtherUserControl C#
public event EventHandler ButtonClicked;
private void Button_Click(object sender, RoutedEventArgs e)
{
if(this.ButtonClicked != null)
{
this.ButtonClicked(this, EventArgs.Empty);
}
}
MainWindow XAML
<StackPanel>
<vm:MyUserControl x:Name="MyUserControl"/>
<vm:OtherUserControl x:Name="OtherUserControl"/>
</StackPanel>
MainWindow C#
public MainWindow()
{
InitializeComponent();
this.OtherUserControl.ButtonClicked += OtherUserControl_ButtonClicked;
}
void OtherUserControl_ButtonClicked(object sender, EventArgs e)
{
this.MyUserControl.TextValue.Text = "Updated Text";
}
Another option is to use something like the Prism Event Aggregator which will allow MyUserControl to subscribe to events raised by OtherUserControl without requiring that MainWindow setup the communication between the two. This is a much better option on larger projects since it allows your components to truly be loosely coupled.

WPF Listbox style with a button

I have a ListBox that has a style defined for ListBoxItems. Inside this style, I have some labels and a button. One that button, I want to define a click event that can be handled on my page (or any page that uses that style). How do I create an event handler on my WPF page to handle the event from my ListBoxItems style?
Here is my style (affected code only):
<Style x:Key="UsersTimeOffList" TargetType="{x:Type ListBoxItem}">
...
<Grid>
<Button x:Name="btnRemove" Content="Remove" Margin="0,10,40,0" Click="btnRemove_Click" />
</Grid>
</Style>
Thanks!
Take a look at RoutedCommands.
Define your command in myclass somewhere as follows:
public static readonly RoutedCommand Login = new RoutedCommand();
Now define your button with this command:
<Button Command="{x:Static myclass.Login}" />
You can use CommandParameter for extra information..
Now last but not least, start listening to your command:
In the constructor of the class you wish to do some nice stuff, you place:
CommandBindings.Add(new CommandBinding(myclass.Login, ExecuteLogin));
or in XAML:
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static myclass.Login}" Executed="ExecuteLogin" />
</UserControl.CommandBindings>
And you implement the delegate the CommandBinding needs:
private void ExecuteLogin(object sender, ExecutedRoutedEventArgs e)
{
//Your code goes here... e has your parameter!
}
You can start listening to this command everywhere in your visual tree!
Hope this helps
PS You can also define the CommandBinding with a CanExecute delegate which will even disable your command if the CanExecute says so :)
PPS Here is another example: RoutedCommands in WPF
As Arcturus posted, RoutedCommands are a great way to achieve this. However, if there's only the one button in your DataTemplate then this might be a bit simpler:
You can actually handle any button's Click event from the host ListBox, like this:
<ListBox Button.Click="removeButtonClick" ... />
Any buttons contained within the ListBox will fire that event when they're clicked on. From within the event handler you can use e.OriginalSource to get a reference back to the button that was clicked on.
Obviously this is too simplistic if your ListBoxItems have more than one button, but in many cases it works just fine.
You could create a user control (.ascx) to house the listbox. Then add a public event for the page.
Public Event btnRemove()
Then on the button click event in the usercontrol
RaiseEvent btnRemove()
You can also pass objects through the event just like any other method. This will allow your user control to tell your page what to delete.

Categories

Resources