WPF 自定义复合控件的一些记录
1、在自定义控件中如果是组合的控件来实现,我们往往会在OnApplyTemplate() 寻找子控件来进行子控件的一些属性操作。这种情况在大部分时候是没问题的。但是MVVM模式下如果自定义控件中有关依赖属性的PropertyChangedCallback回调函数中操作子控件就会面临 子控件未初始化的困难。这是因为依赖属性的PropertyChangedCallback调用优先级会高于OnApplyTemplate()函数导致。所以我们设计时可以不使用OnApplyTemplate()获取子元素来操作子元素。而是新建自定义控件的依赖属性来,子控件通过TemplateBinding 绑定到父控件的依赖属性上来实现自身属性的变化。如果自身属性需要双向通知父元素,我们可以使用"{Binding xx, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" 来实现。

2.在设置子控件的依赖属性时,需注意不能再其回调函数中直接再次修改本身的值,因为在鼠标进行操作时双向绑定会先导致属性值发生变化,但是其触发后再次触发导致后面修改的值会被前值给替换导致无法设置成功,我们可以借助UI线程的异步来解决:当前封装的自定义控件中子控件为一个Combobox,我们绑定其SelectedIndex到ComboboxIndex。
public int ComboboxIndex
{
get { return (int)GetValue(ComboboxIndexProperty); }
set { SetValue(ComboboxIndexProperty, value); }
}
public static readonly DependencyProperty ComboboxIndexProperty =
DependencyProperty.Register("ComboboxIndex", typeof(int), typeof(UnitTextBox2), new PropertyMetadata(0, (s, e) =>
{
UnitTextBox2 current = (UnitTextBox2)s;
if ((int)e.NewValue != -1)
{
//判断当前选项不在需要的范围内,重新设置ComboboxIndex
current.Dispatcher.BeginInvoke(() => { 更新ComboboxIndex值});
}
}));
3.注意集合类型的依赖属性不能设置属性的默认值,不然会应用到所有控件实例上,可以在构造函数中使用SetValue()函数为集合依赖属性赋值。
4.自定义控件样式
<ItemsControl x:Name="PART_DigitsContainer" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" HorizontalContentAlignment="Right" FocusVisualStyle="{x:Null}" Focusable="True" IsTabStop="True" ItemsSource="{Binding Digits, RelativeSource={RelativeSource TemplatedParent}}" KeyboardNavigation.DirectionalNavigation="Contained" KeyboardNavigation.TabNavigation="Once"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel VerticalAlignment="Center" IsItemsHost="True" Orientation="Horizontal" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <ContentControl Focusable="False"> <Border Padding="0" Background="Transparent" BorderThickness="0"> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Consolas" FontSize="22" Foreground="AntiqueWhite" Text="{Binding Value, Converter={StaticResource DigitConverter}}"> <TextBlock.Style> <Style TargetType="TextBlock"> <Setter Property="Background" Value="Transparent" /> <Setter Property="Width" Value="12" /> <Setter Property="Padding" Value="1" /> <Setter Property="Margin" Value="1" /> <Style.Triggers> <DataTrigger Binding="{Binding IsSelected}" Value="True"> <Setter Property="Background" Value="#526970" /> <Setter Property="Foreground" Value="WhiteSmoke" /> </DataTrigger> <DataTrigger Binding="{Binding IsSplited}" Value="True"> <Setter Property="Margin" Value="5,1,1,1" /> </DataTrigger> <DataTrigger Binding="{Binding Value}" Value="-1"> <Setter Property="Text" Value="." /> </DataTrigger> <DataTrigger Binding="{Binding Value}" Value="-2"> <Setter Property="Text" Value="-" /> <Setter Property="Width" Value="10" /> </DataTrigger> <DataTrigger Binding="{Binding IsHighlighted}" Value="false"> <Setter Property="Opacity" Value="0.5" /> </DataTrigger> </Style.Triggers> </Style> </TextBlock.Style> </TextBlock> </Border> </ContentControl> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
DataTemplate 中要使用DataTrigger 触发后改变样式 需要先把样式放入Style中,如果直接写在控件中则不会生效。
<Style TargetType="TextBlock"> <Setter Property="Background" Value="Transparent" /> <Setter Property="Width" Value="12" /> <Setter Property="Padding" Value="1" /> <Setter Property="Margin" Value="1" />

浙公网安备 33010602011771号