WPF Button, Command Target, Binding Question - wpf

I have a DesignerCanvas (derived from canvas) that I can add UIElements to, then drag\drop\move\group and move them around.
On the toolbar I have a button that is bound to a group command.
<Button Margin="0,3,0,3" Padding="5" HorizontalContentAlignment="Left"
Command="{x:Static s:DesignerCanvas.Group}"
CommandTarget="{Binding ElementName=DesignerCanvas}">
The problem that I have is that I can have a control that also contains a DesignerCanvas. So there are nested canvas's, and I want the GroupCommand on the toolbar to apply to the canvas that is in focus. In the above binding it is binding only to the root canvas.
I suppose I could track the current canvas and expose it in the viewmodel for the binding, but I'd prefer to avoid tracking the activecanvas.
Any suggestions for a creative binding here?
Thanks,
jeff

Is GroupCommand a RoutedCommand? Assuming it is, I would expect that you would get the behavior you want by removing the CommandTarget property assignment.

Related

Why do I need to use a lookless control instead of a UserControl when I have swappable sub-parts

I have a WPF application with some custom controls (defined inside the same project) that have swappable sub parts. As a basic example, lets says I have some Xaml like:
<Border Background="White" CornerRadius="9">
<ContentPresenter/>
</Border>
Which is used for my class derived from ContentControl, lets call it MrWhiteControl
If I make MrWhiteControl a xaml and code-behind file pair (so it has an InitializeComponent() call in the ctor), then whatever I place in the Content property has lots of problems e.g. the DataContext won't inherit properly, and using ElementName in bindings won't work.
However, if I use a lookless control - so a MrWhiteControl.cs file with the style defined in Themes/Generic.xaml (I don't care about theming support in this app) - then everything works fine.
What I want to know is why this is the case. What's going on behind the scenes that means the lookless control works fine, but that the xaml with code-behind doesn't work properly?
I've created an example project you can clone from GitHub. If you run that, you'll see that the ElementName binding doesn't work inside the control that has a code-behind (2nd in the list), but does work fine with the lookless control (bottom of the list).
What difference is there between lookless and user controls hosting a content presenter?
Edit: Added a solution to get the binding to work with this approach at the bottom.
Nice question.
AFAICT this is just because of the way the DP is setup and when the Binding is resolved respecting NameScope's.
From MainWindow.xaml you're assigning the DP MyContent property for the control with code-behind which takes the entire TextBox code as the DP value. Hence the binding is not resolved at this point.
When the Binding is applied, in the scope of the UserControl, the ElementName is not found, which we can verify if we add another TextBox into that UserControl, say
<StackPanel>
<TextBox Name="textBox" Text="Sampleeeeeeee" />
<ContentPresenter Content="{Binding MyContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type LookslessVsUserControl:MrWhiteWithCodeBehind}}}" />
</StackPanel>
Now we get
as the point the Binding is resolved the new TextBox is the one which is in scope not the one from MainWindow.xaml
As for the Style, the scope remains the same where the Style is applied, so it finds the MainWindow.xaml's text-box. We hence only have the one level of nesting, which we can also see from Snoop
Solution:
If this is the approach you prefer to set the DP with the control, you can still get the Binding to work:
In your MainWindow.xaml.cs you need to set the namescope for the UserControl accordingly so it doesn't use it's own WPF Xaml NameScope
In ctor() of MainWindow.xaml.cs:
Loaded += (sender, args) => NameScope.SetNameScope(problemControl, NameScope.GetNameScope(this));
and in xaml I named it problemControl such as:
<LookslessVsUserControl:MrWhiteWithCodeBehind x:Name="problemControl">
<LookslessVsUserControl:MrWhiteWithCodeBehind.MyContent>
<TextBlock Text="{Binding ElementName=textBox, Path=Text}" />
</LookslessVsUserControl:MrWhiteWithCodeBehind.MyContent>
</LookslessVsUserControl:MrWhiteWithCodeBehind>
With this when the UserControl loads and tries to resolve the Binding it should find the TextBox fine and give you the output you desire.

Loading more items when scrollbar scrolled to the end in ListView(WPF)

I want to implement a function that using a ListView to load items, but the number of items are very large, so I want when the user scroll the scrollbar to the end of the ListView, it auto load more items. I have found a solution to detect if the scroll is scrolled to the end here: Detect when WPF listview scrollbar is at the bottom? But in MVVM, I didn't find a solution to pass EventArgs. Is there any other solutions?
My Xaml looks like this:
<ScrollViewer>
<ListView>
...
</ListView>
</ScrollViewer>
Thanks!
You could have your View execute an ICommand Property on your ViewModel and take advantage of the CommandParameter parameter of the Execute method. However, I would warn that passing the state of your View to the ViewModel so that the ViewModel can determine which items to load is not an appropriate MVVM pattern. Generally, the ViewModel needs to drive the show, even if that includes offloading some UI state information from the View to the ViewModel so that it can natively deduce what to load.
If you use MVVMLight in your WPF project, just set PassEventArgsToCommand true.
eg:
xmlns:ni="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mv="http://www.galasoft.ch/mvvmlight"
<ni:Interaction.Triggers>
<ni:EventTrigger EventName="SelectionChanged">
<mv:EventToCommand Command="{Binding YourCommand}" PassEventArgsToCommand="True" />
</ni:EventTrigger>

Showing UserControl once at the time with DataTemplateSelector

I have a couple specific user controls to Show some Content, e.g. simple like Image, WebControl but also two complex specific custom controls drawing on a canvas.
Now I thought using the DataTemplateSelector to handle the different UserControls. I actully used this http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector as a reference.
I changed the code so the form loads the UserControls dynamically (according to the file extension) in the following collection:
ObservableCollection<string> _pathCollection = new ObservableCollection<string>();
The only difference to the reference is now I want to navigate back and forward to the next control by showing one control only at the time. Which control should I use instead of ListView?
<Grid>
<ListView ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding ElementName=This, Path=PathCollection}"
ItemTemplateSelector="{StaticResource imgStringTemplateSelector}">
</ListView>
</Grid>
How do I need to bind it to the template (equal to ItemTemplateSelector above)? WPF is still very new to me and I am learning.
Use a ContentControl. Bind your current item to the Content-property and the DataTemplateSelector to the ContentTemplateSelector-property.
<ContentControl Content="{Binding Path=CurrentItem, Mode=OneWay}", ContentTemplateSelector="{StaticResource imgStringTemplateSelector}" />
Your CurrentItem should be a DependencyProperty or a INotifyPropertyChanged-property of your DataContext. When you change your CurrentItem, the ContentControl will update the template automatically with help of your TemplateSelector.

How to bind StackPanel Children from a ViewModel?

New to Silverlight. I'm working on a chat application where new chat messages are added to the bottom of a list. I had a working version that used as StackPanel inside a ScrollViewer and then in some code behind used StackPanel.Children.Add().
I'm trying to convert this to a View-ViewModel approach, and I can't figure out how to bind the Children of the StackPanel to any collection property. I've tried this:
<ScrollViewer Name="scrollMessages" Grid.Row="2" Margin="0,0,0,0" VerticalScrollBarVisibility="Visible">
<StackPanel x:Name="pnlMessages" Orientation="Vertical" Children="{Binding Path=ExampleTBs}" />
</ScrollViewer>
where ExampleTBs is a collection of TextBlocks created in code. This fails XAML parsing, the Children property isn't bindable in this way.
Is the approach of binding to the StackPanel itself fixable? Should I be using a different container type? I saw another question where the guy created the entire StackPanel in code and then used a ContentPresenter...
Bottom line, I'd like to find a way to databind my view to a viewmodel using something like a StackPanel as a container where successive items will be added to the container over time. Best approach?
Use a ListBox (or any other ItemsControl) and bind the ItemsSource property to an ObservableCollection in your ViewModel.
Do you need to use a StackPanel? If you use an ItemsControl instead, this still presents each chat message in a vertical list, and also allows for binding of the data.

How do I bind a command to e.g. the right mouse button in a ControlTemplate in WPF?

I have a custom Control derived class and a view model to go with. The user can do several actions with this control, and I'm thinking it's best to implement these as RoutedCommand objects or ICommand derived objects in the view model so the ControlTemplates can bind to them. Binding a command to a button in one ControlTemplate should be straightforward, but how can I bind the command to e.g. the right mouse button in another ControlTemplate? Probably a MouseGesture is involved, but I'm having a hard time fitting the pieces together.
A MouseGesture is involved, but you don't have to explicitly construct it. You can use a MouseBinding which will construct a MouseGesture for you under the hood.
You need a UIElement to attach your binding to. Here is how you would do it with a separate decorator.
<ControlTemplate ...>
<Decorator>
<Decorator.InputBindings>
<MouseBinding MouseAction="RightClick" Command="..." />
</Decorator.InputBindings>
... content here ...
</Decorator>
</ControlTemplate>
More likely your ControlTemplate uses a Panel such as a DockPanel or Grid for layout, in which case you could attach the binding to that instead of adding a Decorator.

Resources