Tips: Chinese content is just a translation of English content, you don't need to care about them, and please don't delete them
提示: 英文内容只是中文内容的翻译, 你不需要关心它们, 也请不要删掉它们
Knowing that I now have an MVVM MAUI project. There is a BindingList<string> Names property in the ViewModel, which generates controls in the view like this:
已知我现在有一个 MVVM 的 MAUI 项目. 在 ViewModel 中有一个 BindingList<string> Names 属性, 它会在视图中这样进行生成控件:
<CollectionView ItemsSource="{Binding Names}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I want after each item, there is a Button that can be used to delete the current item, and the Button logic is called via Command.
That is to say, in the logic of this Command, operate the Names property in ViewModel and delete one of them, so this Command needs to be passed into ViewModel
我希望在每一项后, 都有一个 Button 可以用来删除当前项, 并且 Button 逻辑通过 Command 调用.
也就是说, 这个 Command 的逻辑中, 操作 ViewModel 中的 Names 属性, 删除其中一项, 所以这个 Command 需要传入 ViewModel
Normally, if you declare the Command directly in the ViewModel, and then instantiate it, create a new Command and pass in the current ViewModel object, so that the Command can manipulate the Names in the ViewModel
正常来讲, 如果直接在 ViewModel 中声明这个 Command, 然后实例化的时候, 新建 Command 并传入当前 ViewModel 对象, 这样 Command 就可以操作这个 ViewModel 中的 Names
However, for CollectionView, his DataTemplate binding cannot be bound to the properties of the BindingContext set by Page, and his BindingContext is each item in Names. In this way, the instance of Command cannot be obtained.
但是, 对于 CollectionView, 他的 DataTemplate 绑定是无法绑定到 Page 设定的 BindingContext 的属性的, 他的 BindingContext 是 Names 中的每一项. 这样就拿不到 Command 的实例
If you define it in Resource, you can get the Command instance through StaticResource, but how does ViewModel pass in this Command?
如果通过在 Resource 中定义的话, 倒是可以通过 StaticResource 来拿到 Command 实例, 但是, ViewModel 改如何传入这个 Command 呢?
I want to achieve this using MVVM design pattern
我想要使用 MVVM 设计模式实现这一目的
Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MyViewModel}}, Path=DetailsCommand}"
You want this.
Edit: To clarify. You are tapping on a DataTemplate bound to specific model. With this you are telling to go search a parent of this what you are tapping (your ViewModel) and use its command. (Since your Model doesn't have any commands).
First, I noticed that you want to use the MVVM, so you can use the ObservableCollection instead of using BindingList. You can refer to this article about ObservableCollection.
Second, you can use the relative-bindings as the solution.
<Button Text="Delete"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:MainPageViewModel}}, Path=DeleteItemCommand}"
CommandParameter="{Binding}"/>
Third, you can also set a name for your page and bind the name to your button source.
<Button Text="Delete" Command="{Binding BindingContext.DeleteCommand, Source={x:Reference MainPage}}" CommandParameter="{Binding .}" />
I hope I got your problem right.
The way out of the textbook:
Don't use "Binding Names" but bind to ViewModels again. The default should always be to bind to ViewModels.
But sometimes for me this was too much work and what you can do is to use the DataContext of a parent element. The nicest way for me it to give the root element a name and bind to this like:
{Binding DataContext.YourProperty, ElementName=YourRootElement}
With CommandParameter={Binding} you get your element.
[edit] You can also use FindAnchestor, but personally I don't likethat as it easily breaks when you change your XAML structure.
[edit2] It seems MAUI does not support binding by ElementName, at least I couldn't find it. So you will have to use the anchestor binding type https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/relative-bindings#bind-to-an-ancestor
PS: Use BindingList only if your really need it, otherwise use ObservableCollection
I have a Prism application and I'm attempting to bind PreviewMouseDown and PreviewMouseUp button events in my view to commands in my view model. When I run the code I see the following exception:
As a workaround, I'm currently binding to methods in the view and use a reference to the data context of the view model to execute the command. This works but doesn't seem correct because the view now has knowledge of the view model.
What is the proper way to handle something like this?
You cannot bind events to commands like, for example, the Command property of a button.
Luckily, you don't need to, because you have the Command property. It even disables the button if the command returns false from CanExecute.
If you have something other than a button or something other than MouseDown, you can use InvokeCommandAction (from Prism or from Interactivity)...
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<prism:InvokeCommandAction Command="{Binding MyCommand}"/>
<!-- or -->
<i:InvokeCommandAction Command="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
I'm implementing a delayed load treeview, and need to handle the Expanded event. I would like to do it directly in the ViewModel. I have hooked up an EventTrigger:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Expanded">
<interactivity:InvokeCommandAction Command="{Binding HandleExpandCmd}" TriggerParameterPath="OriginalSource" />
</i:EventTrigger>
</i:Interaction.Triggers>
While this works as expected, the OriginalSource is a TreeViewItem, and I would prefer to not include System.Windows.Controls in my ViewModel.
Alternatively, I could handle the event in code-behind, and pass the TreeViewItems's DataContext to the ViewModel. (The DataContext, of course, is what the ViewModel needs to work with.)
Is there a syntax I can use in the TriggerParameterPath that passes the TVI's DataContext, instead of the TVI itself?
Thanks ---
I would suggest to bind the TreeViewItem's IsExpanded property to one in your ViewModel. Then you can handle it in the ViewModel however you want!
Here is an example of how to do that.
I am implementing MVVM pattern in wpf application. We have a textbox that calls event handler on KeyUp event. I binded text of that textbox with property tb_property which is in ViewModel. The idea that I have is to have ViewModel implement INotifyPropertyChange and to bind command object that does what event handler did and implements ICommand to the change on that tb_property. Is this a good idea, and how should I do it? I am trying to get rid of most of the events from View. Thank you in advance.
Yes, depending on what MVVM framework you are using you can bind the control's event to a iCommand on the viewmodel.
I use MVVM light so they have a build in realaycommand for just such a condition. I then bind the control's event int he following way:
add the following to your usercontrol/window opening tag (the cmd uses the mvvm framworks implementation of icommand):
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
Then I add the command binding to the control like:
<TextBox Grid.Row="1" Text="{Binding tb_property, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="YourEvent">
<cmd:EventToCommand Command="{Binding YourCommandPropertyOnVIewModel}" CommandParameter="OptionalCommandParameter"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
This way you move all your "event" to your view model and I love this because other controls can use the same command.
Hope this helps
JK
I am having trouble and don't know what to pass as command parameter.
<triggers:Interactions.Triggers>
<triggers:EventTrigger EventName="ItemClick">
<triggers:InvokeCommandAction Command="{Binding MenuItemClick}" CommandParameter=""/>
</triggers:EventTrigger>
</triggers:Interactions.Triggers>
This is inside gridview declaration. I want to recieve gridview clicked item in binded delegate as parameter.
You can set the PassEventArgsToCommand property of the InvokeCommandAction to true which will allow to pass ItemClickEventArgs pp to your command.
<triggers:InvokeCommandAction
Command="{Binding MenuItemClick}" PassEventArgsToCommand="True"/>
or see this article which provides more reliable solution from view point of better design.