【问题标题】:Access InkToolbar placed in parent element from programmatically generated child elements从以编程方式生成的子元素访问放置在父元素中的 InkToolbar
【发布时间】:2021-10-30 16:30:40
【问题描述】:

我有一个UserControl,其中包含一个InkToolbar。在这个控件中,我根据用户输入生成另一组UserControls。在每个以编程方式生成的控件中,都包含一个InkCanvas

这是父 UserControl 和背后的代码。

Main.xaml

<UserControl
    x:Class="uwp.Main"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:uwp_mvvm"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Grid.Row="0">
            <InkToolbar x:Name="InkToolbar" Background="Black" Margin="10 0 0 0" VerticalAlignment="Center"/>
        </StackPanel>

        <ScrollViewer ZoomMode="Enabled" Background="DarkGray" x:Name="ScrollViewer" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" MinZoomFactor="0.25" Width="Auto" Height="Auto" MaxZoomFactor="4" Grid.Row="1">
            <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
                <ItemsControl x:Name="PagesItemsControl" ItemsSource="{x:Bind Pages, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Top">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapGrid Orientation="Vertical"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <local:TestControl x:Name="TestControl" Page="{Binding}" Margin="4 4" InkToolbarControl="{Binding Path=InkToolbar, ElementName=InkToolbar}" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </ScrollViewer>
    </Grid>
</UserControl>

Main.xaml.cs

public sealed partial class MainViewer : UserControl
{
    public MainViewer()
    {
        this.InitializeComponent();
    }

    private void EventTriggerChanged(object sender, PropertyChangedEventArgs e)
    {
        var trigger = sender as EventTriggerTestControl;
        if (trigger != null)
            RaiseChanged(e.PropertyName);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaiseChanged(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    private PdfDocument pdfDocument = null;
    private string password = string.Empty;
    private ObservableCollection<PdfPage> pages = new ObservableCollection<PdfPage>();

    public string Password
    {
        private get { return password; }
        set { if (value != password) { password = value; } }
    }

    public ObservableCollection<PdfPage> Pages
    {
        get { return pages; }
        set { if (value != pages) { pages = value; } }
    }

    public StorageFile File
    {
        get { return (StorageFile)GetValue(FileProperty); }
        set { SetValue(FileProperty, value); }
    }

    public static readonly DependencyProperty FileProperty =
        DependencyProperty.Register(nameof(File), typeof(StorageFile), typeof(MainViewer), new PropertyMetadata(null, new PropertyChangedCallback(OnDocumentChanged)));

    private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (object.Equals(e.NewValue, e.OldValue) || e.NewValue is null)
            return;

        d.RegisterPropertyChangedCallback(FileProperty, CaptureDocument);
    }

    private async static void CaptureDocument(DependencyObject sender, DependencyProperty dp) => await (sender as MainViewer).OpenFileAsync(sender.GetValue(dp) as StorageFile);

    private async Task OpenFileAsync(StorageFile file)
    {
        try
        {
            if (file == null)
                throw new ArgumentNullException(nameof(file));

            var files = await ApplicationData.Current.LocalFolder.GetFilesAsync(CommonFileQuery.OrderByName);

            if (files.Where(x => x.Name == file.Name).ToList().Count == 0)
                await file.CopyAsync(ApplicationData.Current.LocalFolder, file.Name, NameCollisionOption.ReplaceExisting);

            file = await ApplicationData.Current.LocalFolder.GetFileAsync(file.Name);
            pdfDocument = new PdfDocument(file.Path, Password);

            pdfDocument.Pages.ToList().ForEach(x => Pages.Add(x));
        }
        catch (Exception ex) { throw ex; }
    }
}

我想使用TestControl 内的父控件中的InkToolbar

TestControl.xaml

<UserControl
    x:Class="uwp.TestControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="using:uwp_mvvm.ViewModels" xmlns:converter="using:uwp_mvvm"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <UserControl.Resources>
        <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
    </UserControl.Resources>

    <Grid x:Name="Viewport" VerticalAlignment="Top" HorizontalAlignment="Center">
        <Border x:Name="ViewportBorder" Background="White" BorderThickness="2, 2, 2, 2" BorderBrush="#FF353334" />
        <Image x:Name="Image" Margin="0" UseLayoutRounding="True" ManipulationMode="Scale"/>
        <Canvas x:Name="SelectionCanvas" CompositeMode="MinBlend" Opacity="1" />
        <InkCanvas x:Name="InkCanvas" />
    </Grid>
</UserControl>

TastControl.xaml.cs

public sealed partial class TestControl : UserControl
{
    private const float DPI = 256;

    public PdfPage Page
    {
        get { return (PdfPage)GetValue(PageProperty); }
        set { SetValue(PageProperty, value); }
    }

    public static readonly DependencyProperty PageProperty =
        DependencyProperty.Register(nameof(Page), typeof(PdfPage), typeof(TestControl), new PropertyMetadata(null, new PropertyChangedCallback(OnPageChanged)));

    private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (object.Equals(e.NewValue, e.OldValue) || e.NewValue is null)
            return;

        d.RegisterPropertyChangedCallback(PageProperty, CapturePage);
    }

    private static void CapturePage(DependencyObject sender, DependencyProperty dp) => (sender as TestControl).RenderPage(sender.GetValue(dp) as PdfPage);
    
    public InkToolbar InkToolbarControl
    {
        get { return (InkToolbar)GetValue(InkToolbarControlProperty); }
        set { SetValue(InkToolbarControlProperty, value); }
    }

    public static readonly DependencyProperty InkToolbarControlProperty =
            DependencyProperty.Register(nameof(InkToolbarControl), typeof(InkToolbar), typeof(TestControl), new PropertyMetadata(null));

    public TestControl()
    {
        this.InitializeComponent();

        ConfigureInkCanvas();
    }

    private void ConfigureInkCanvas()
    {
        InkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Touch;
        InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.None;
        InkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(
            new InkDrawingAttributes
            {
                IgnorePressure = false,
                FitToCurve = true,
                Color = Colors.Black,
                PenTip = PenTipShape.Circle
            });

        InkCanvas.InkPresenter.UnprocessedInput.PointerEntered -= InkCanvasUnprocessedInputPointerEntered;
        InkCanvas.InkPresenter.UnprocessedInput.PointerEntered += InkCanvasUnprocessedInputPointerEntered;

        InkCanvas.InkPresenter.UnprocessedInput.PointerExited -= InkCanvasUnprocessedInputPointerExited;
        InkCanvas.InkPresenter.UnprocessedInput.PointerExited += InkCanvasUnprocessedInputPointerExited;
    }

    private void InkCanvasUnprocessedInputPointerExited(InkUnprocessedInput sender, PointerEventArgs args)
    {
        //InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.Inking;
        //InkToolbarControl.TargetInkCanvas = InkCanvas;
    }

    private void InkCanvasUnprocessedInputPointerEntered(InkUnprocessedInput sender, PointerEventArgs args)
    {
        //InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.None;
        //InkToolbarControl.TargetInkCanvas = null;
    }

    private void RenderPage(PdfPage page, float dpi = DPI)
    {
        if (page is null)
            return;

        this.DataContext = page;
        var width = (DataContext as PdfPage).Width;
        var height = (DataContext as PdfPage).Height;

        var writableBitmap = new WriteableBitmap((int)(width / 72f * dpi), (int)(height / 72f * dpi));
        (DataContext as PdfPage).Render(writableBitmap, PageOrientations.Normal, RenderingFlags.Annotations);

        Image.Width = width;
        Image.Height = height;
        Image.Source = writableBitmap;

        SelectionCanvas.Width = width;
        SelectionCanvas.Height = height;

        InkCanvas.Width = width;
        InkCanvas.Height = height;

        Canvas.SetZIndex(Image, 0);

        ConfigureInkCanvas();
    }
}

我尝试使用DependencyPropertyInkToolbar 发送到TestControl,但它似乎不起作用。

有人可以帮我解决这个问题吗?我不确定是否也可以像这样设置TargetInkCanvas。因此,如果对此有更好的方法,我们也欢迎任何建议。

非常感谢任何帮助。谢谢。

【问题讨论】:

    标签: c# xaml uwp uwp-xaml


    【解决方案1】:

    从以编程方式生成的子元素访问放置在父元素中的 InkToolbar

    上述使用 DependencyProperty 传递父 InkToolBar 的解决方案是正确的。但是,您为 InkToolbarControl 属性绑定了错误的值。

    请直接绑定不带路径的ElementName。

    <local:TestControl
        x:Name="TestControl"
        Margin="4,4"
        InkToolbarControl="{Binding ElementName=InkToolbar}" />
    

    然后更新InkCanvasUnprocessedInputPointerExitedInkCanvasUnprocessedInputPointerEntered流程逻辑。

    private void InkCanvasUnprocessedInputPointerExited(InkUnprocessedInput sender, PointerEventArgs args)
    {
        InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.Inking;
        InkToolbarControl.TargetInkCanvas = null;
    }
    
    private void InkCanvasUnprocessedInputPointerEntered(InkUnprocessedInput sender, PointerEventArgs args)
    {
        InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.None;
        InkToolbarControl.TargetInkCanvas = InkCanvas;
    }
    

    【讨论】:

    • 谢谢尼科。现在只有我看到我已经配置了 PointerEnteredPointerExited 事件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-29
    • 2018-12-09
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多