为什么多个IsEnabled绑定无法工作?

时间:2016-04-20 17:03:55

标签: c# wpf data-binding

作为选项卡内容控件的DataContext的主ViewModel包含大量属性。模型本身实现了一个继承INotifyPropertyChanged的抽象类。每个属性集都可以通过抽象类的方法运行。因此,他们都已完全注册了通知,并且大部分工作正常。

我有一个布尔属性绑定到内容控件上许多控件的IsEnabled属性。这是因为用户在运行服务器查询时不应更改值。所有控件的绑定完全相同:

IsEnabled="{Binding Path=IsIndicative}"

几乎每个像这样的控件都是RadComboBox(Telerik)。只有 1 才能响应绑定。在屏幕截图中,"无"组合框被禁用,但所有其他组合都没有。它们是完全相同的类型,它们都使用相同的父DataContext属性。

Binding Failure

我无法确定为什么其中一个人正在响应绑定,但其他人却忽略了它。您也可以看到日期下拉菜单,但这些日期下拉菜单会嵌套在一个视图中,我稍后会再次使用它。

我需要帮助确定IsEnabled绑定的内容以及忽略它的原因。

附加说明:我甚至在VS2012中添加了代码隐藏作为解决方法,但现在甚至可能已经停止工作了!

     StrategyTypeComboBox.IsEnabled = isEnabled;
     QuantityNumericUpDown.IsEnabled = isEnabled;
     datePicker.IsEnabled = isEnabled;
     OptionsOrderTypeComboBox.IsEnabled = isEnabled;
     ExpirationTypeComboBox.IsEnabled = isEnabled;
     DeltaHedgeTypeComboBox.IsEnabled = isEnabled;
     TopPriceTypeToggle.IsEnabled = isEnabled;
     PriceInTypeComboBox.IsEnabled = isEnabled;

XAML的示例

所有控件都具有相同的精确设置。唯一真正的区别是它们所在的Grid.Row。显然,TextBlock和RadComboBox有一些差异,但布局和控制方向完全相同。

        <Grid Grid.Row="0" Margin="5,3,5,3">
           <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" /> <!-- strategy_type label -->
              <ColumnDefinition Width="*" /> <!-- dotted line -->
              <ColumnDefinition Width="Auto" SharedSizeGroup="TopLeftGridValues" /> <!-- StrategyTypeComboBox -->
           </Grid.ColumnDefinitions>
           <TextBlock Foreground="{Binding Path=MainLabelTextColor}" Style="{StaticResource LabelStyle}" Grid.Column="0" Margin="5,0,2,0"  Height="Auto">
              <TextBlock.Text>
                 <Binding Path="strategy_type" Source="{x:Static util:Strings.Instance}" />
              </TextBlock.Text>
           </TextBlock>
           <Line Grid.Column="1" Style="{StaticResource DottedLineStyle}" />
           <telerikInput:RadComboBox Grid.Column="2" Style="{StaticResource LabelStyle}" MinWidth="60" Margin="3,0,3,0" HorizontalAlignment="Stretch"
                                     x:Name="StrategyTypeComboBox"
                                     IsEnabled="{Binding Path=IsIndicative}"
                                     Command="{x:Static ptcommands:OrderCommands.StrategyChanged}"
                                     CommandTarget="{Binding RelativeSource={RelativeSource Self}}"
                                     ItemsSource="{Binding Path=StrategyTypesCollection}"
                                     DisplayMemberPath="display"
                                     SelectedValuePath="value"
                                     SelectedIndex="{Binding Path=StrategyTypeSelectedIndex}">
              <telerikInput:RadComboBox.ItemsPanel>
                 <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                 </ItemsPanelTemplate>
              </telerikInput:RadComboBox.ItemsPanel>

           </telerikInput:RadComboBox>
        </Grid> <!-- Row 0: Strategy -->

1 个答案:

答案 0 :(得分:0)

其他人发布的答案指出我朝着正确的方向走了。所以,我正在创建这个答案,以防其他人遇到同样的问题。

关键问题是Command属性及其相应的CanExecute功能将覆盖IsEnabled属性。在我们的例子中,CanExecute方法总是返回true,因此控件不会禁用。集中式基本视图处理所有命令绑定,因此未编码以专门检查命令是否真的可以执行。还有一个中心ViewModel绑定到该视图,以便在整个窗口中使用。

要解决此问题,我必须执行以下操作:

  • 在ViewModel上创建布尔属性,可以使用与绑定的特定于选项卡的值相同的值从选项卡管理器设置。
  • 添加一些List<string>字段以包含应受各种值影响的控件名称。我可能会更改它来检查命令本身(类似于我们的Executed处理程序的工作方式),但这可以完成工作。
  • 更改CanExecute处理程序以检索源控件的名称,然后返回适当的值。
  • 从内容控件中删除所有IsEnabled绑定。

控件名称在初始化时填充,如下所示:

     controlsSell = new List<string>();
     controlsSell.AddRange(new string[] {
                                          "sellButton",
                                          "OptionsSellButton"
                                        });

     controlsBuy = new List<string>();
     controlsBuy.AddRange(new string[] {
                                         "buyButton",
                                         "OptionsBuyButton"
                                       });

最终的CanExecute处理程序最终看起来像这样。

  /// <summary>
  /// Determines if the command can be executed based on TicketViewModel properties.
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  public void CanExecuteCustomCommand(object sender,
      CanExecuteRoutedEventArgs e)
  {
     // NOTE: This may need more complex handling such as in ExecutedCustomCommand.
     //       Right now, it is dependent on specific control names, which is not optimal.

     // This is usually always PTTicketView, not the originating control.
     Control target = e.Source as Control;
     // This is the control that actually executes the command.
     Control orig = e.OriginalSource as Control;

     if (orig == null)
     {
        e.CanExecute = false;
     }
     else
     {
        string ctlName = orig.Name;

        if (dataContext == null)
        {
           e.CanExecute = true;
        }
        else
        {
           if (dataContext.SelectedTab != TicketViewModel.Tabs.MultiLeg)
           {
              e.CanExecute = true;
           }
           else
           {
              if (controlsBuy.Contains(ctlName))
              {
                 e.CanExecute = dataContext.IsCommandBuyEnabled;
              }
              else if (controlsSell.Contains(ctlName))
              {
                 e.CanExecute = dataContext.IsCommandSellEnabled;
              }
              else if (controlsOther.Contains(ctlName))
              {
                 e.CanExecute = dataContext.IsCommandOtherEnabled;
              }
              else if (controlsExpiries.Contains(ctlName))
              {
                 e.CanExecute = (dataContext.IsExpiriesEnabled && dataContext.IsIndicative);
              }
              else if (ctlName.Equals("DealableTypeToggle"))
              {
                 e.CanExecute = dataContext.IsRFSEnabled;
              }
              else
              {
                 e.CanExecute = true;
              }
           }
        }
     }
  }

通过此实现,所有控件都会根据值正确更改IsEnabled状态。