1

我想做的很简单,当 TextBox 有焦点时显示一种格式,而在没有焦点时显示另一种格式。在我的情况下,我在不集中时将一个数字四舍五入为 3 位数字,但在集中于编辑时显示实际的整数。

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

我正在使用这种样式来传递我的绑定以及 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;

    }

}
4

1 回答 1

0

对于它的价值,我从这篇CodeProject 文章中得出了一个解决方案。关键是使用样式中的触发器来切换内容模板。提供的示例并不完美,但它是一次很好的学习体验。

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

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

编辑

这是完整 XAML 中更好的方法。我在我的博客上写了一篇相应的文章

<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}"
       />
于 2012-08-18T14:50:38.517 回答