WPF。 .NET 4.6.1
以下代码正确创建了一个由InkCanvas和背景线的多个WriteBox组成的装饰器。它正确绑定到视图模型中的属性。 Code by Josh Smith
<ac:AdornedControl IsAdornerVisible="{Binding TranscriptionLayer.IsAdornerVisible}" Grid.ColumnSpan="2">
<!--#region Adorned Element-->
<!--
TranscriptionLayer will have an Adorner (inkcanvas) for handwriting recogntion.
The RichTextBox is the element being adorned and sets the size of the adornment object (i.e.,
the inkcanvas writing surface). The RichTextControl holds the actual transcript.
-->
<RichTextBox x:Name="RichTextControl" Panel.ZIndex="{Binding TranscriptionLayer.ZIndex}" Cursor="IBeam"
AllowDrop="True" PreviewDragEnter="RichTextControl_PreviewDragEnter"
PreviewDragOver="RichTextControl_PreviewDragOver"
PreviewDrop="RichTextControl_PreviewDrop"
Height="{Binding VirtualPage.Height}"
Visibility="{Binding TranscriptionLayer.TranscriptIsVisible}"
SpellCheck.IsEnabled="True"
VerticalScrollBarVisibility="Auto"
AcceptsReturn="True" AcceptsTab="True"
>
<!--Remove blank line between paragraphs-->
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</RichTextBox.Resources>
<i:Interaction.Behaviors>
<!--
Update the menu and toolbar when a selection is made in the RichTextBox
The behavior is bound to the SelectionChanged of the RichTextBox.
The normal binding sends the binding source to the dependency property, the target, of the behavior.
BindsTwoWayByDefault sends the behavior value, the target, back to the Binding source.
-->
<!--<b:RichTextBehavior AlignLeft ="{Binding TranscriptionLayer.AlignLeft}" /> -->
<b:RichTextBehavior
FontHeight="{Binding ElementName=Fontheight, Path=SelectedItem, Mode=TwoWay, Converter={c:NullToDoubleConverter}}"
TextFont="{Binding ElementName=Fonttype, Path=SelectedItem}"
TextBold="{Binding ElementName=ToggleBold, Path=IsChecked}"
Italic="{Binding ElementName=ToggleItalic, Path=IsChecked}"
Underline="{Binding ElementName=ToggleUnderline, Path=IsChecked}"
Strikethrough="{Binding ElementName=ToggleStrikethrough, Path=IsChecked}"
ParagraphTag ="{Binding ElementName=CurrentParagraph, Path=SelectedItem}"
IsFocused ="{Binding TranscriptionLayer.RichTextHasFocus}"
MoveText ="{Binding TranscriptionLayer.MoveText}"
SelectedText="{Binding TranscriptionLayer.SelectedText}"
IsImage="{Binding TranscriptionLayer.IsImage}"
DeleteImage="{Binding TranscriptionLayer.DeleteImage}"
/>
</i:Interaction.Behaviors>
</RichTextBox>
<!--#endregion-->
<ac:AdornedControl.AdornerContent>
<!--#region The Adorner-->
<!-- This is the framework element as the adorner content. It is always on top of the adorned element. There adorned elements
is the RichTextConttrol.
The ItemsControl will expand to the width of the parent adorner which takes its size from the adorned element-the
RichTextConttrol. The ItemsControl inherits from System.Windows.FrameworkElement.
-->
<ItemsControl x:Name="WritingLayerControl" ItemsSource="{Binding TranscriptionLayer.WritingBoxes}" >
<!--
If the <ItemsControl.ItemsPanel> is not used, the ItemsControl will default to a vertical StackPanel.
-->
<ItemsControl.ItemTemplate>
<!--
DataTemplate and DataType point to a class, not a namespace!
-->
<DataTemplate DataType="{x:Type vm:WritingBoxViewModel}" >
<Grid>
<Grid.RowDefinitions>
<!-- 0 to be used for recogntion results-->
<RowDefinition Height="auto" />
<!-- 1 to be used for Ink -->
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding RecognitionResults}" Background="#100000FF"
Height="{Binding RecognitionResultsHeight}"/>
<!--
Binding Mode must be set to TwoWay on TheSelectedStrokes because by default binding works one way,
i.e. loading changes from the view model, but not updating the viewmodel back. So either use:
TheSelectedStrokes="{Binding SelectedStrokes, Mode=TwoWay}" or set it with
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault in TheSelectedStrokes dependency definition.
Background is of type Brush, so must use ImageBrush, DrawingBrush, VisualBrush.
-->
<InkCanvas x:Name="InkCanvas" Grid.Row="1"
Height="{Binding WritingBoxHeight}" Strokes="{Binding Strokes}"
EditingMode="{Binding EditingMode}"
DefaultDrawingAttributes="{Binding DefaultDrawingAttributes}" >
<i:Interaction.Behaviors>
<b:InkCanvasBehavior DeletedStrokes="{Binding DrawingLayer.DeleteStrokes}" />
</i:Interaction.Behaviors>
<InkCanvas.Background>
<!--
Note: Making the DrawingBrush a StaticResource makes the XAML much more efficient as only one object
is created. It also is only created on the first pass and no update from the Bindings will happen so
the lines will not change even when the TypeSize is changed.
Viewport (type Rect) gives the position, width, and height of the base tile
-->
<DrawingBrush Stretch="Uniform" TileMode="Tile" Viewport="{Binding ViewPortBaseTile}" ViewportUnits="Absolute" >
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<!--
X is horizontal displacement from origin.
Y is veritcal displacyement from origin.
Origin O(0,0) is top-left of InkCanvas.
-->
<!-- Vertical Line
<LineGeometry StartPoint="0,0" EndPoint="{Binding TileBottom}"/>
-->
<!-- Midline. Horizontal Line -->
<LineGeometry StartPoint="{Binding MidLineStartPoint}" EndPoint="{Binding MidLineEndPoint}"/>
<!-- BaseLine. Horizontal Line-->
<LineGeometry StartPoint="{Binding BaseLineStartPoint}" EndPoint="{Binding BaseLineEndPoint}"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Tomato"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</InkCanvas.Background>
</InkCanvas>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--#endregion-->
</ac:AdornedControl.AdornerContent>
</ac:AdornedControl>
现在,我想改变:
<ItemsControl x:Name="WritingLayerControl" ItemsSource="{Binding TranscriptionLayer.WritingBoxes}" >
因此“WritingBoxes”属性位于Adorner类本身 - 而不是在viewmodel中。在viewmodel中,它被定义为:
private ObservableCollection<WritingBoxViewModel> _writingBoxes;
public ObservableCollection<WritingBoxViewModel> WritingBoxes
{
get { return _writingBoxes; }
set { if (_writingBoxes == value) return; _writingBoxes = value; RaisePropertyChanged(); }
}
问题:如何将AdornedControl中的ItemsSource绑定到Adorner中的属性? (请记住,在任何特定时间,可能会或可能不会看到装饰者。)
TIA