1

我想要做的很简单,当TextBox具有焦点时显示一种格式,而当文本框没有焦点时显示另一种格式。在我的情况下,我将一个数字四舍五入为3位数字,但不显示焦点时显示实际的整数,以便进行编辑。当使用MultiBinding聚焦时TextBox的不同格式不会更新源

我有一个相当简单的解决方案,使用多重绑定,我觉得我几乎在那里。一切都按预期工作,在即时窗口中没有错误,但绑定不会更新源。

我正在使用此样式来传递我的绑定以及TextBox是否具有焦点到转换器。

<Style x:Key="RoundedTextBox" TargetType="{x:Type ContentControl}"> 
    <Setter Property="Focusable" Value="False"/> 
    <Setter Property="ContentTemplate"> 
     <Setter.Value> 
      <DataTemplate> 
       <TextBox x:Name="TB" TextAlignment="Right" DataContext="{TemplateBinding Content}"> 
        <TextBox.Text> 
         <MultiBinding Converter="{StaticResource DecRounder}" UpdateSourceTrigger="PropertyChanged"> 
          <MultiBinding.Bindings> 
           <Binding ElementName="TB" Path="DataContext" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" BindsDirectlyToSource="True" /> 
           <Binding ElementName="TB" Path="IsFocused" Mode="OneWay" /> 
          </MultiBinding.Bindings> 
         </MultiBinding> 
        </TextBox.Text> 
       </TextBox> 
      </DataTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

实例:

<ContentControl Style="{StaticResource RoundedTextBox}" 
       Content="{Binding Path=Model.SomeNumber}" /> 

和多值转换器在这里。

public class DecimalRoundingConverter : IMultiValueConverter 
{ 

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 

     if (values.Length != 2) 
      throw new Exception("Invalid number of values for DecimalRoundingConverter!"); 

     if (values[0] is string) 
      return values[0]; 

     if (values[0] != null && !(values[0] is decimal)) 
      throw new Exception("Invalid type for first value used with DecimalRoundingConverter!"); 
     if (!(values[1] is bool)) 
      throw new Exception("Invalid type for second value used with DecimalRoundingConverter!"); 
     if (targetType != typeof(string)) 
      throw new Exception("Invalid target type used with DecimalRoundingConverter!"); 

     if (values[0] == null) 
      return null; 

     decimal number = (decimal)values[0]; 

     bool isFocused; 
     if (values[1] == null) 
      isFocused = true; 
     else if (values[1] is bool) 
      isFocused = (bool)values[1]; 
     else 
      if (!bool.TryParse((string)values[1], out isFocused)) 
       throw new Exception("Invalid converter parameter used with DecimalRoundingConverter!"); 

     return string.Format(isFocused ? "{0:.#############################}" : "{0:.###}", number); 

    } 

    public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 

     decimal d; 
     var ret = new object[2]; 

     if (value == null) 
      ret[0] = null; 
     else if (decimal.TryParse((string)value, out d)) 
      ret[0] = d; 
     else 
      ret[0] = value; 

     ret[1] = Binding.DoNothing; 

     return ret; 

    } 

} 

回答

0

对于它的价值,我来源于此CodeProject article的解决方案。关键是在样式中使用触发器来切换内容模板。提供的例子并不完美,但这是一次很好的学习经历。

此方法的唯一缺点是ContentTemplates和Style必须在UserControl中定义,因为ContentTemplates直接指向TextBox事件处理程序。这是因为对TextBox的引用必须传递给后面的代码。当您尝试覆盖样式时,您将失去切换ContentTemplate的触发器。

这个缺点对我来说很好,因为我绑定到重要属性的应用程序设置,比如ContentStringFormat。

编辑

下面是完整的XAML一个更好的方法。我写了一个corresponding article on my blog

<ControlTemplate x:Key="EditableDecimalTemplate" TargetType="{x:Type ContentControl}"> 
    <ContentPresenter Name="contentHolder" 
          VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
          RecognizesAccessKey="True" 
          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> 
     <ContentPresenter.Content> 
      <Grid Margin="0"> 
       <Border Name="nonFocusedBorder" 
         Grid.ZIndex="3" IsHitTestVisible="False" 
         BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
         Background="{TemplateBinding Background}" 
         /> 
       <TextBox Name="editTextBox" 
          Grid.ZIndex="1" Opacity="0" 
          Margin="0" Padding="{TemplateBinding Padding}" 
          HorizontalAlignment="Stretch" VerticalAlignment="Center" 
          TextAlignment="Right" 
          Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, Mode=TwoWay}" 
          BorderThickness="{TemplateBinding BorderThickness}" 
          /> 
       <Border BorderBrush="{x:Null}" Height="{Binding ElementName=editTextBox, Path=ActualHeight}" Margin="0,0,3,0" 
         Padding="{TemplateBinding BorderThickness}"> 
        <TextBlock Name="displayTextBlock" 
           Grid.ZIndex="2" IsHitTestVisible="False" 
           VerticalAlignment="Center" HorizontalAlignment="Stretch" 
           Margin="{TemplateBinding Padding}" 
           TextAlignment="Right" 
           Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, StringFormat={}{0:#.###;-#.###;0}, Mode=OneWay}" 
           /> 
       </Border> 
       <Border/> 
      </Grid> 
     </ContentPresenter.Content> 
    </ContentPresenter> 
    <ControlTemplate.Triggers> 
     <Trigger SourceName="editTextBox" Property="IsKeyboardFocused" Value="True"> 
      <Setter TargetName="displayTextBlock" Property="Opacity" Value="0" /> 
      <Setter TargetName="editTextBox" Property="Opacity" Value="1" /> 
      <Setter TargetName="nonFocusedBorder" Property="Visibility" Value="Collapsed"/> 
     </Trigger> 
     <Trigger Property="BorderThickness" Value="0"> 
      <Setter TargetName="editTextBox" Property="BorderThickness" Value="1" /> 
      <Setter TargetName="nonFocusedBorder" Property="BorderThickness" Value="1" /> 
      <Setter TargetName="nonFocusedBorder" Property="BorderBrush" Value="Transparent" /> 
     </Trigger> 
    </ControlTemplate.Triggers> 
</ControlTemplate> 

<Style x:Key="EditableDecimalLabel" TargetType="{x:Type Label}"> 
    <Setter Property="Template" Value="{StaticResource EditableDecimalTemplate}" /> 
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/> 
    <Setter Property="VerticalContentAlignment" Value="Stretch"/> 
    <Setter Property="Padding" Value="4" /> 
    <Setter Property="FontFamily" Value="Consolas, Lucida Console, Courier New"/> 
    <Setter Property="TextElement.FontSize" Value="13" /> 
    <Setter Property="SnapsToDevicePixels" Value="True"/> 
    <Setter Property="BorderThickness" Value="1" /> 
    <Style.Triggers> 
     <Trigger Property="IsMouseOver" Value="True"> 
      <Setter Property="BorderBrush" Value="#B5CFFF"/> 
     </Trigger> 
    </Style.Triggers> 
</Style> 

而且样品用量:

<Label Name="TestControl" 
     Width="120" 
     Content="{Binding Path=MyNumber, Mode=TwoWay}" 
     Style="{StaticResource EditableDecimalLabel}" 
     />