1

私がやりたいことは簡単です。TextBox にフォーカスがある場合は 1 つの書式を表示し、フォーカスがない場合は別の書式を表示します。私の場合、フォーカスされていないときは数値を 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 の記事から解決策を導き出しました。キーは、スタイルでトリガーを使用してコンテンツ テンプレートを切り替えることです。提供された例は完璧ではありませんが、良い学習経験でした。

このアプローチの唯一の欠点は、ContentTemplates が TextBox イベント ハンドラーを直接参照するため、ContentTemplates と Style を UserControl で定義する必要があることです。これは、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 に答える