gpt4 book ai didi

c# - 当 CaretIndex 在 MVVM 中更改时,我没有收到通知

转载 作者:行者123 更新时间:2023-11-30 22:11:19 25 4
gpt4 key购买 nike

我知道 CaretIndex 不是依赖属性。

所以我注册如下:

 public class TextBoxHelper : TextBox
{
public static readonly DependencyProperty CaretIndexProperty
= DependencyProperty.Register
(
"CaretIndex",
typeof(int),
typeof(TextBoxHelper),
new FrameworkPropertyMetadata
(
0,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
CaretIndexChanged
)
);

public static int GetCaretIndex(DependencyObject obj)
{
return (int)obj.GetValue(CaretIndexProperty);
}

public static void SetCaretIndex(DependencyObject obj, int value)
{
obj.SetValue(CaretIndexProperty, value);
}

//public new int CaretIndex
//{
// get { return (int)GetValue(CaretIndexProperty); }
// set { SetValue(CaretIndexProperty, value); }
//}

protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
CaretIndex = base.CaretIndex;
}

protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
CaretIndex = base.CaretIndex;
}

protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
CaretIndex = base.CaretIndex;
}

protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
CaretIndex = base.CaretIndex;
}

protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
CaretIndex = base.CaretIndex;
}

private static void CaretIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj is TextBox)
{
((TextBox)obj).CaretIndex = (int)e.NewValue;
}
}
}

然后我在我的 ViewModel 中创建了一个名为 CaretIndex 的属性。它实现了 INotifyPropertyChanged 接口(interface)。

private int _caretIndex;
public int CaretIndex
{
get { return _caretIndex; }
set
{
_caretIndex = value;
OnPropertyChanged("CaretIndex");
}
}

然后我在我的 ComboBox 中创建绑定(bind),如下所示:

<ComboBox x:Name="cbUnder" ItemsSource="{Binding GroupsAndCorrespondingEffects}" 
IsEditable="True" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}"
Text="{Binding InputValue, UpdateSourceTrigger=PropertyChanged}" TextSearch.TextPath="GroupName"
Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="3"
vm:TextBoxHelper.CaretIndex="{Binding CaretIndex, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type vm:GroupAndCorrespondingEffect}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" Width="250">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="{Binding CorrespondingEffect}" />
</StackPanel>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>

当 CaretIndex 发生变化时,我仍然没有收到通知。

从上面的代码中,我得到了 IsEditable = TrueIsTextSearchEnabled = True

但是当我在组合框中键入任何字符时,它会在组合框的文本框中附加整个项目的名称。

我实际上想要一个组合框来突出显示与我键入的内容匹配的所有项目,但由于文本附加,我只能突出显示一个项目。

所以,我需要输入的文本。即未选择的文本。 (因为附加的文本也被选中)。

所以,如果我得到了 caretIndex,那么我就可以在文本上使用 substring 方法来获取我键入的内容。据此,它将突出显示文本。

更新

<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<TextBox x:Name="PART_EditableTextBox" CaretIndex="vm:TextBoxHelper.CaretIndex" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

我试过上面的代码,但它说我输入的字符串格式不正确。

所以,我将上面的代码替换为:

<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<TextBox x:Name="PART_EditableTextBox" vm:TextBoxHelper.CaretIndex="{Binding CaretIndex, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

现在我没有收到任何错误,但我的组合框看起来像一个文本框。我的意思是它已经失去了它的下拉部分。我已经在我的 View 模型中使用断点进行了检查。但是当 caretIndex 发生变化时,我仍然没有收到通知。

更新2

<TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="vm:TextBoxHelper.CaretIndex" Value="{Binding CaretIndex, UpdateSourceTrigger=PropertyChanged}" />
.
.
.

上面的代码不工作,所以我更新如下:

<vm:TextBoxHelper x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
<vm:TextBoxHelper.Style>
<Style TargetType="{x:Type vm:TextBoxHelper}">
<Setter Property="vm:TextBoxHelper.CaretIndex" Value="{Binding CaretIndex, UpdateSourceTrigger=PropertyChanged}" />
.
.
.

还是不行。

更新 3

这是我的 App.xaml 文件

<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:comboFDD="clr-namespace:ERP_Lite_Trial.Views.DesignRelatedCode"
x:Class="ERP_Lite_Trial.App" StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Views/Pages/ResourceDictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="comboFDD:ComboBox_ForceDropDown.OpenDropDownAutomatically" Value="True"/>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

更新 4

我的 ViewModel 中也有一个 InputValue 属性

private string _inputValue;
public string InputValue
{
get { return _inputValue; }
set
{
_inputValue = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");

for (int i = 0; i < GroupsAndCorrespondingEffects.Count; i++)
{

string WordToSearch = _inputValue;

if (_caretIndex != 0 || _caretIndex != null)
{
WordToSearch = _inputValue.Substring(0, _caretIndex);
}

GroupsAndCorrespondingEffects[i].IsHighlighted = GroupsAndCorrespondingEffects[i].GroupName.StartsWith(WordToSearch);
}
}
}

我获得的另一个属性是 IsHilighted。此属性在 GroupsAndCorrespondingEffects 类中定义。我使用此类从数据库中获取数据。

GroupsAndCorrespondingEffects.cs 代码

public class GroupAndCorrespondingEffect : INotifyPropertyChanged
{
private string _groupName;
public string GroupName
{
get
{
return _groupName;
}
set
{
_groupName = value;
OnPropertyChanged("GroupName");
}
}

private string _correspondingEffect;
public string CorrespondingEffect
{
get
{
return _correspondingEffect;
}
set
{
_correspondingEffect = value;
OnPropertyChanged("CorrespondingEffect");
}
}

private bool _isHighlighted;
public bool IsHighlighted
{
get
{
return _isHighlighted;
}
set
{
_isHighlighted = value;
OnPropertyChanged("IsHighlighted");
}
}

public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

public event PropertyChangedEventHandler PropertyChanged;
}

在ViewModel中调用属性的过程如下:

现在当我在组合框中输入第一个字符时:

CaretIndex = 0

CaretIndex = 0

InputValue = Text of combobox

CaretIndex = 1

CaretIndex = 1

当我在组合框中输入第二个字符时:

CaretIndex = 1

InputValue = Text of combobox

CaretIndex = 2

当我在组合框中输入第三个字符时:

CaretIndex = 2

InputValue = Text of combobox

CaretIndex = 3

等等....

我在组合框中突出显示项目的逻辑是基于 CaretIndex 的变化。此逻辑写在 InputValue 属性的设置部分。但是由于上面提到的在 CaretIndex 取新值之前调用 InputValue 的事实,我得到了不正确的 HighLights。

更新 5 - 保留丢失的选择

我在 TextBoxHelper 类中添加了 SelectionStart 和 SelectionLength 属性,因为它们不是依赖属性。

代码如下:

    public static readonly DependencyProperty BindableSelectionStartProperty
= DependencyProperty.RegisterAttached
(
"BindableSelectionStart",
typeof(int),
typeof(TextBoxHelper),
new PropertyMetadata
(
BindableSelectionStartChanged
)
);

public static readonly DependencyProperty BindableSelectionLengthProperty
= DependencyProperty.RegisterAttached
(
"BindableSelectionLength",
typeof(int),
typeof(TextBoxHelper),
new PropertyMetadata
(
BindableSelectionLengthChanged
)
);

public static int GetBindableSelectionStart(DependencyObject obj)
{
return (int)obj.GetValue(BindableSelectionStartProperty);
}

public static void SetBindableSelectionStart(DependencyObject obj, int value)
{
obj.SetValue(BindableSelectionStartProperty, value);
}

public int BindableSelectionStart
{
get
{
return (int)this.GetValue(BindableSelectionStartProperty);
}
set
{
this.SetValue(BindableSelectionStartProperty, value);
}
}

public static int GetBindableSelectionLength(DependencyObject obj)
{
return (int)obj.GetValue(BindableSelectionLengthProperty);
}

public static void SetBindableSelectionLength(DependencyObject obj, int value)
{
obj.SetValue(BindableSelectionLengthProperty, value);
}

public int BindableSelectionLength
{
get
{
return (int)this.GetValue(BindableSelectionLengthProperty);
}
set
{
this.SetValue(BindableSelectionLengthProperty, value);
}
}

private static void BindableSelectionStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox)
{
((TextBox)d).SelectionStart = (int)e.NewValue;
}
}

private static void BindableSelectionLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox)
{
((TextBox)d).SelectionLength = (int)e.NewValue;
}
}

protected override void OnSelectionChanged(RoutedEventArgs e)
{
base.OnSelectionChanged(e);
BindableSelectionStart = base.SelectionStart;
BindableSelectionLength = base.SelectionLength;
}

然后我在我的 ViewModel 中创建了相应的属性。

    private int _selectionStart;
public int SelectionStart
{
get
{
return _selectionStart;
}
set
{
_selectionStart = value;
OnPropertyChanged("SelectionStart");
}
}

private int _selectionLength;
public int SelectionLength
{
get
{
return _selectionLength;
}
set
{
_selectionLength = value;
OnPropertyChanged("SelectionLength");
}
}

之后,我更改了 ViewModel 的 CaretIndexInputValue 属性,如下所示:

    private int _caretIndex;
public int CaretIndex
{
get { return _caretIndex; }
set
{
_caretIndex = value;
OnPropertyChanged("CaretIndex");

if (InputValue != null && CaretIndex >= 0)
{
SelectionStart = CaretIndex;
Debug.WriteLine(string.Format("Selection Start : {0}", SelectionStart));
SelectionLength = InputValue.Length - CaretIndex;
Debug.WriteLine(string.Format("Selection Length : {0}", SelectionLength));
}

for (int i = 0; i < GroupsAndCorrespondingEffects.Count; i++)
{

string WordToSearch = InputValue;

if (_caretIndex != 0 && _caretIndex > 0)
{
WordToSearch = InputValue.Substring(0, _caretIndex);
}

if (WordToSearch != null)
{
GroupsAndCorrespondingEffects[i].IsHighlighted = GroupsAndCorrespondingEffects[i].GroupName.StartsWith(WordToSearch);
}
}
}
}

private string _inputValue;
public string InputValue
{
get { return _inputValue; }
set
{
_inputValue = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");

if (InputValue != null && CaretIndex >= 0)
{
SelectionStart = CaretIndex;
Debug.WriteLine(string.Format("Selection Start : {0}", SelectionStart));
SelectionLength = InputValue.Length - CaretIndex;
Debug.WriteLine(string.Format("Selection Length : {0}", SelectionLength));
}

for (int i = 0; i < GroupsAndCorrespondingEffects.Count; i++)
{

string WordToSearch = _inputValue;

if (_caretIndex != 0 && _caretIndex > 0 && _caretIndex < _inputValue.Length)
{
WordToSearch = _inputValue.Substring(0, _caretIndex);
}

GroupsAndCorrespondingEffects[i].IsHighlighted = GroupsAndCorrespondingEffects[i].GroupName.StartsWith(WordToSearch);

}
}
}

我对 ResourceDictionary 所做的最后更改如下:

<vm:TextBoxHelper x:Name="PART_EditableTextBox" MyCaretIndex="{Binding CaretIndex}" 
BindableSelectionStart="{Binding SelectionStart}"
BindableSelectionLength="{Binding SelectionLength}"
...........

在输出窗口中,我可以看到 SelectionStart 和 SelectionLength 发生了变化,但在组合框中看不到任何视觉变化。

最佳答案

您的代码存在一些问题:

1) 通过在 TextBoxHelper 中执行 CaretIndex = base.CaretIndex,您实质上是在设置相同的属性,不会触发或更改任何内容。

2) 您正在设置 ComboBox 默认模板,但是当 IsEditable 为真时,您想要设置的是 ComboBox 模板。否则,WPF 会将您提供的模板作为默认模板,一旦您将 IsEditable 设置为 true,WPF 就会引入默认的 Editable 模板,该模板使用 TextBox,而不是用户 TextBoxHelper。

所以,这就是我为让它工作所做的:

1) 更新 ComboBox 样式以仅当 IsEditableTrue

时设置您的模板
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<Style.Triggers>
<Trigger Property="IsEditable" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<local:TextBoxHelper x:Name="PART_EditableTextBox" MyCaretIndex="{Binding CaretIndex}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>

2) 将 TextBoxHelper 中的 CaretIndex 属性的名称更改为 MyCaretIndex,代码如下:

public sealed class TextBoxHelper: TextBox
{
public static readonly DependencyProperty MyCaretIndexProperty
= DependencyProperty.Register
(
"MyCaretIndex",
typeof(int),
typeof(TextBoxHelper),
new FrameworkPropertyMetadata
(
0,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
MyCaretIndexChanged
)
);

public static int GetMyCaretIndex(DependencyObject obj)
{
return (int)obj.GetValue(MyCaretIndexProperty);
}

public static void SetMyCaretIndex(DependencyObject obj, int value)
{
obj.SetValue(MyCaretIndexProperty, value);
}

public int MyCaretIndex
{
get { return (int)GetValue(MyCaretIndexProperty); }
set { SetValue(MyCaretIndexProperty, value); }
}

protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
MyCaretIndex = base.CaretIndex;
}

protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
MyCaretIndex = base.CaretIndex;
}

protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
MyCaretIndex = base.CaretIndex;
}

protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
MyCaretIndex = base.CaretIndex;
}

protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
MyCaretIndex = base.CaretIndex;
}

private static void MyCaretIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj is TextBox)
{
((TextBox)obj).CaretIndex = (int)e.NewValue;
}
}
}

还有我的 CaretIndex 属性的 ViewModel 代码供您引用:

public int CaretIndex
{
get { return _caretIndex; }
set
{
_caretIndex = value;
Trace.WriteLine(String.Format("Caret Index {0}", _caretIndex));
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("CaretIndex"));
}
}

查看代码:

<ComboBox ItemsSource="{Binding Items}" IsEditable="True" />

通过上述更改,我可以在我的 View 模型中看到正确的 CaretIndex 值。

请注意,使用该可编辑模板时 ComboBox 看起来并不漂亮(它没有切换按钮来下拉包含所有值的弹出窗口)。但是,我想这不在这个问题的范围内,但是,如果您也想要这个,请告诉我。

更新

我使用 Blend 提取了 ComboBox 的默认模板,并将 TextBox 替换为 TextBoxHelper。我删除了几个阴影边框,因为它们指的是 PresentationFramework.Aero2.dll。

这是完整的模板和模板引用的所有资源,非常冗长:

<LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0.0"/>
<GradientStop Color="#FFE5E5E5" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFECF4FC" Offset="0.0"/>
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFEBF4FC" Offset="0.0"/>
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFDAECFC" Offset="0.0"/>
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="#FF569DE5"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFDAEBFC" Offset="0.0"/>
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
<Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="ClickMode" Value="Press"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="templateRoot" BorderBrush="{StaticResource ComboBox.Static.Border}" BorderThickness="{TemplateBinding BorderThickness}" Background="{StaticResource ComboBox.Static.Background}" SnapsToDevicePixels="true">
<Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="true" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
<Path x:Name="arrow" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z" Fill="{StaticResource ComboBox.Static.Glyph}" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center"/>
</Border>
</Border>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.MouseOver.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Pressed.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Disabled.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Border}"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Style.Triggers>
<Trigger Property="IsEditable" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
<Border x:Name="border" Margin="{TemplateBinding BorderThickness}">
<local:TextBoxHelper x:Name="PART_EditableTextBox" MyCaretIndex="{Binding CaretIndex}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>

更新 2

修复选中文本丢失问题:

TextBoxHelper 中的 MyCaretIndexChanged 方法更新为:

private static void MyCaretIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj is TextBox && (int)e.OldValue != (int)e.NewValue)
{
var textBox = (TextBox) obj;
textBox.CaretIndex = (int)e.NewValue;
if (!string.IsNullOrEmpty(textBox.Text))
textBox.Select(textBox.CaretIndex, textBox.Text.Length - textBox.CaretIndex);
}
}

关于c# - 当 CaretIndex 在 MVVM 中更改时,我没有收到通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20302351/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com