Привязка к положению подвижного UserControl в WPF - PullRequest
0 голосов
/ 04 февраля 2020

Я работаю над оверлейным приложением, которое имеет ряд интерактивных элементов, реализованных в виде пользовательских элементов управления внутри прозрачного родительского окна.

Я хочу привязать к позиции этих (перетаскиваемых) пользовательских элементов управления чтобы сохранить их как пользовательские настройки

Вещи, которые я пробовал:

  • привязать Margin к свойству Thickness, которое не обновляется при перемещении элемента управления
  • привязка Canvas.Left / Top непосредственно к свойствам настроек (можно найти как комментарий в xaml), которые вообще не работают

Родительское окно:

<Window
x:Class="PoeSuite.Overlay"
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:local="clr-namespace:PoeSuite.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:p="clr-namespace:PoeSuite.Properties"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:il="clr-namespace:Microsoft.Expression.Interactivity.Layout;assembly=Microsoft.Expression.Interactions"
AllowsTransparency="True"
Background="{x:Null}"
DataContext="{Binding OverlayCanvas, Source={StaticResource Locator}}"
Deactivated="Window_Deactivated"
Activated="Window_Activated"
SourceInitialized="Window_SourceInitialized"
ShowInTaskbar="False"
Visibility="{Binding ShouldBeVisible, Mode=TwoWay, Converter={StaticResource BoolToVis}}"
WindowState="Maximized"
WindowStyle="None"
mc:Ignorable="d">

<Grid>
        <!--
        Canvas.Left="{Binding Source={x:Static p:Settings.Default}, Path=IncomingOverlayX, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        Canvas.Top="{Binding Source={x:Static p:Settings.Default}, Path=IncomingOverlayY, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        -->
    <local:IncomingRequests
        Margin="{Binding RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor, AncestorLevel=1}, Path=DataContext.IncomingOverlayPosition}">
        <i:Interaction.Behaviors>
            <il:MouseDragElementBehavior />
        </i:Interaction.Behaviors>
    </local:IncomingRequests>
</Grid>
</Window>

Контроль пользователя:

<UserControl
    x:Class="PoeSuite.Views.IncomingRequests"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:converters="clr-namespace:PoeSuite.Views.Converters"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Name="incomingRequestsWindow"
    DataContext="{Binding IncomingRequests, Source={StaticResource Locator}}"
    Visibility="{Binding ShouldBeVisible, Mode=TwoWay, Converter={StaticResource BoolToVis}}"
    mc:Ignorable="d">

    <Grid>
        <TabControl
            ItemsSource="{Binding ActiveRequests, Mode=TwoWay}"
            SelectedItem="{Binding SelectedTab, Mode=OneWayToSource}">

            <TabControl.Resources>
                <converters:ItemsControlIndexConverter x:Key="IndexConverter" />
                <converters:BooleanToColorConverter x:Key="BooleanToColor" />
            </TabControl.Resources>

            <TabControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock
                                MaxWidth="50"
                                Foreground="{Binding PlayerJoinedArea, Converter={StaticResource BooleanToColor}}"
                                Text="{Binding ItemName}">
                            </TextBlock>
                            <Button
                                Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor, AncestorLevel=1}, Path=DataContext.CloseTabCommand}"
                                CommandParameter="{Binding}"
                                Content="x" />
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <Grid>
                        <TextBlock Text="{Binding PlayerName, Mode=OneWay}" />
                        <Border BorderThickness="5" BorderBrush="AliceBlue">
                            <DockPanel
                            Grid.Row="1"
                            Width="Auto"
                            Height="20">
                                <Button
                                Width="20"
                                HorizontalAlignment="Left"
                                VerticalAlignment="Top"
                                Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor, AncestorLevel=1}, Path=DataContext.SendInviteCommand}"
                                Content="+" />
                                ...
                            </DockPanel>
                        </Border>                        
                    </Grid>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
</UserControl>

и соответствующий ViewModel :

    public class OverlayViewModel : ViewModelBase
    {
        private bool _shouldBeVisible;

        public bool ShouldBeVisible
        {
            get
            {
                return _shouldBeVisible;
            }
            set
            {
                _shouldBeVisible = value;
                RaisePropertyChanged(nameof(ShouldBeVisible));
            }
        }

        public Thickness IncomingOverlayPosition
        {
            get
            {
                return new Thickness(Properties.Settings.Default.IncomingOverlayX,
                    Properties.Settings.Default.IncomingOverlayY, 555, 555);
            }
            set
            {
                Properties.Settings.Default.IncomingOverlayX = value.Left;
                Properties.Settings.Default.IncomingOverlayY = value.Top;

                RaisePropertyChanged(nameof(IncomingOverlayPosition));
            }
        }

        public OverlayViewModel()
        {
            if (IsInDesignMode)
                _shouldBeVisible = true;
        }
    }
...