Wpf属性绑定vs命令

Wpf属性绑定vs命令,wpf,binding,command,Wpf,Binding,Command,我创建了两个用户控件,一个在画布上显示我的数据/视图模型集合的二维笛卡尔地图。另一个是一栏滚动图像库,它为每个数据模型显示一个图像。(片段化)映射基于: <ItemsControl ItemsSource="{Binding Defects}" ItemTemplateSelector="{StaticResource IconSelector}"> <!-- ItemsPanelTemplate --&

我创建了两个用户控件,一个在画布上显示我的数据/视图模型集合的二维笛卡尔地图。另一个是一栏滚动图像库,它为每个数据模型显示一个图像。(片段化)映射基于:

<ItemsControl 
        ItemsSource="{Binding Defects}"
        ItemTemplateSelector="{StaticResource IconSelector}">            
        <!-- ItemsPanelTemplate -->
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas x:Name="MapCanvas" Height="10000"/>                       
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <!-- ItemContainerStyle -->
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
                <Setter Property="Canvas.Left" Value="{Binding X}" />                     
            </Style>               
        </ItemsControl.ItemContainerStyle>
</ItemsControl>
<ScrollViewer Name="ScrollContainer"
        Grid.Column="1" PanningMode="VerticalOnly" 
        VerticalScrollBarVisibility="Visible"
        HorizontalScrollBarVisibility="Visible"                  
        Background="White">
    <ItemsControl Name="ItemsContainer" ItemsSource="{Binding Defects}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border BorderThickness="5" BorderBrush="Gray" Margin="2">
                    <Image Source="{Binding ImageUrl}" Margin="2"></Image>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

图像库(同样是片段化的)基于:

<ItemsControl 
        ItemsSource="{Binding Defects}"
        ItemTemplateSelector="{StaticResource IconSelector}">            
        <!-- ItemsPanelTemplate -->
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas x:Name="MapCanvas" Height="10000"/>                       
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <!-- ItemContainerStyle -->
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
                <Setter Property="Canvas.Left" Value="{Binding X}" />                     
            </Style>               
        </ItemsControl.ItemContainerStyle>
</ItemsControl>
<ScrollViewer Name="ScrollContainer"
        Grid.Column="1" PanningMode="VerticalOnly" 
        VerticalScrollBarVisibility="Visible"
        HorizontalScrollBarVisibility="Visible"                  
        Background="White">
    <ItemsControl Name="ItemsContainer" ItemsSource="{Binding Defects}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border BorderThickness="5" BorderBrush="Gray" Margin="2">
                    <Image Source="{Binding ImageUrl}" Margin="2"></Image>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

现在我想给我的数据的这两个视图添加一些同步。也就是说,当单击地图上的一个图标时,我希望图库滚动到相应的图像。 我已尝试向两个控件添加依赖项属性,以指向选定的索引。这并不是我真正想要的,因为我想要一些额外的灵活性。有时我希望滚动与动画一起完成,有时我希望立即同步选定的索引。 因此,我尝试将自定义命令BringIntoView添加到viewmodel中。此命令接受布尔输入,以指定是否必须使用动画执行操作。我可以将datatemplate绑定到我的自定义命令,并且只要单击地图上的图标,地图控件就会正确地调用自定义命令上的execute

但是我被困在gallery端,如何将此命令绑定到某个代码隐藏方法,或者绑定到控件上的某个其他方法?命令绑定不起作用,因为它不允许对命令进行动态绑定。以下内容引发编译时错误,因为CommandBinding上的Command属性不是依赖项属性:

<UserControl.CommandBindings>
    <CommandBinding Command="{Binding BringIntoView}" Executed="OnBringIntoViewExecuted"/>
</UserControl.CommandBindings> 

我可以添加一个静态命令并直接引用该命令,但这至少会将视图绑定到viewmodel命令。那么,如何实现这种级别的命令链接,同时保持与视图的解耦呢


换句话说,WPF依赖属性系统允许绑定两个UI控件的两个UI特定属性。如何实现命令/事件的相同功能?也就是说,来自用户控件的UI事件/命令连接到另一个UI控件的方法/命令?

我找到了解决此设计问题的方法。与依赖属性类似,自定义控件也可以公开自定义命令。由于控件直接公开的命令(与viewmodel公开的命令相反)是特定于UI的,因此最适合使用RoutedUiCommand而不是自定义ICommand实现。RouteDicCommand可以作为静态属性公开,就像DependencyProperty一样。在我的示例中,gallery控件公开了一个静态BringIntoView命令,如下所示:

public partial class DefectGallery {
  // the control exposes the following commands:
  public static readonly RoutedUiCommand BringIntoView = new RoutedUiCommand;
  // the control exposes the following depdendency properties:
  public static readonly DependencyProperty ScrollSpeedProperty = DependencyProperty.Register(...);   
  ... 
}  
该命令绑定到DefectGallery控件的xaml文件中的代码隐藏:

<UserControl.CommandBindings>
   <CommandBinding Command="{x:static local:DefectGallery.BringIntoView}" Executed="OnBringIntoViewExecuted"/>
</UserControl.CommandBindings> 

最后,需要一些其他控件来触发bringintoview命令。例如,它可以是WPF提供的命令源,如按钮。在我的例子中,我在map控件上实现了ICommandSource接口,以便允许指定一个自定义命令,该命令在每次单击map上的图标时执行。我现在可以使用xaml中的映射控件(上面的第一个片段),如下所示:

<MapControl 
    Command="{x:static local:DefectGallery.BringIntoView}"
    CommandTarget="{Binding ElementName=defectGalleryInstance}"
 />

因此,我可以有效地使用声明性xaml从另一个控件“调用”控件

现在,这似乎和添加依赖属性一样基本,所以我很惊讶我没有找到这方面的指导方针。在我看来,控件公开属性和命令是很自然的;我认为这两个界面是UI控件与其他UI组件的“接口”,而视图模型绑定是应用层的接口。哦,WPF适合不同的使用方法,您只需找到适合您/您的应用程序的方法