C# 设置相对于 MainWindow 的 translatetransform X 和 Y 属性 [英] C# Set translatetransform X and Y property relative to MainWindow

查看:31
本文介绍了C# 设置相对于 MainWindow 的 translatetransform X 和 Y 属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 ItemsControl 中托管了一个 Wrappanel,它被包装在一个滚动查看器中.包装面板中的项目填充有绑定.每个项目都有一个带有数据触发器的数据模板.该触发器用于将项目设置为屏幕中央的动画.我试图用 translatetransform 来做到这一点,但问题是 translatetransform 的 X 和 Y 属性是相对于项目本身而不是宿主容器的,所以每个项目都有不同的动画.如此处所示:

I have a Wrappanel hosted inside ItemsControl which is wrapped in a scrollviewer. Items in a wrappanel are populated with binding. Every item has a datatemplate with a datatrigger. That trigger is suposed to animate the item to the center of the screen. I was trying to do that with translatetransform, but the problem is that X and Y properties of translatetransform is relative to the item itself and not the host container, so every item has a different animation. As seen here:

webm 1webm 2

项目模板 XAML:

<DataTemplate x:Key="CountryItemTemplate">
    <Grid 
        x:Name="gridMain"
        Height="Auto"
        Width="Auto"
        Margin="3"
        RenderTransformOrigin="0 0"
        Panel.ZIndex="{Binding IsVisible, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
        <materialDesign:Card 
            x:Name="cardMain" 
            Height="350" 
            Width="310"
            RenderTransformOrigin="0.5, 0.5"
            materialDesign:ShadowAssist.ShadowDepth="Depth1"
            UniformCornerRadius="3">
            <Grid Background="Transparent">
                <Grid.RowDefinitions>
                    <RowDefinition Height="50*"/>
                    <RowDefinition Height="50*"/>
                </Grid.RowDefinitions>
                <Rectangle
                    Grid.Row="0">
                    <Rectangle.Fill>
                        <ImageBrush ImageSource="{Binding ImageUrl, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
                    </Rectangle.Fill>
                </Rectangle>
                <TextBlock
                    Grid.Row="1"
                    Foreground="Black"
                    FontWeight="Regular"
                    FontSize="25"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Margin="16 24 0 0"
                    Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                <Button
                    Grid.Row="1"
                    Style="{DynamicResource MaterialDesignToolButton}"
                    Content="Edit"
                    FontSize="15"
                    Width="85"
                    Height="35"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    Margin="0 0 16 24"
                    Command="{Binding IsInEditModeToggleCommand}"/>
            </Grid>
            <materialDesign:Card.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <TranslateTransform/>
                </TransformGroup>
            </materialDesign:Card.RenderTransform>
        </materialDesign:Card>
    </Grid>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding IsInEditMode, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
            <DataTrigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation
                            Storyboard.TargetName="cardMain"
                            Storyboard.TargetProperty="(UIElement.RenderTransform).Children[1].(TranslateTransform.X)"
                            To="300"
                            By="1"
                            Duration="0:0:2">
                            <DoubleAnimation.EasingFunction>
                                <ExponentialEase
                                    EasingMode="EaseInOut"
                                    Exponent="16"/>
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                        <DoubleAnimation
                            Storyboard.TargetName="cardMain"
                            Storyboard.TargetProperty="(UIElement.RenderTransform).Children[1].(TranslateTransform.Y)"
                            To="400"
                            By="1"
                            Duration="0:0:2">
                            <DoubleAnimation.EasingFunction>
                                <ExponentialEase
                                    EasingMode="EaseInOut"
                                    Exponent="16"/>
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

我尝试像这样使用 UIElement.TranslatePoint :

I tried to use UIElement.TranslatePoint like this :

所有东西所在的整个 UserControl:

The whole UserControl where everything is:

<UserControl 
    x:Class="NikolaLukovic.CustomsOfficeApp.Desktop.Views.CountryUserControl"
    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:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:domain="clr-namespace:NikolaLukovic.CustomsOfficeApp.Desktop.DomainModels"
    xmlns:helpers="clr-namespace:NikolaLukovic.CustomsOfficeApp.Desktop.Helpers"
    xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls"
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    xmlns:validationRules="clr-namespace:NikolaLukovic.CustomsOfficeApp.Desktop.Helpers.ValidationRules"
    xmlns:views="clr-namespace:NikolaLukovic.CustomsOfficeApp.Desktop.Views"
    xmlns:viewModels="clr-namespace:NikolaLukovic.CustomsOfficeApp.Desktop.ViewModels"
    xmlns:local="clr-namespace:NikolaLukovic.CustomsOfficeApp.Desktop.Views"
    mc:Ignorable="d" 
    d:DesignHeight="600" 
    d:DesignWidth="900">
    <UserControl.CacheMode>
        <BitmapCache/>
    </UserControl.CacheMode>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadDataCommand}" CommandParameter="{Binding ElementName=icMain}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <UserControl.DataContext>
        <viewModels:CountryViewModel/>
    </UserControl.DataContext>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="../Resources/CountryItemStyle.xaml"/>
                <ResourceDictionary Source="../Resources/Icons.xaml"/>
                <ResourceDictionary Source="../Resources/MaterialDesignIcons.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Button.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.ToggleButton.xaml" />
            </ResourceDictionary.MergedDictionaries>
            <BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <materialDesign:DialogHost>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="100"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="100"/>
            </Grid.ColumnDefinitions>
            <ScrollViewer
                Grid.Row="0"
                Grid.Column="0"
                Grid.RowSpan="2"
                Grid.ColumnSpan="2"
                VerticalScrollBarVisibility="Auto">
                <ItemsControl
                    HorizontalContentAlignment="Stretch"
                    VerticalContentAlignment="Stretch"
                    x:Name="icMain"
                    ItemTemplate="{StaticResource CountryItemTemplate}"
                    ItemsSource="{Binding Countries, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </ScrollViewer>
            <Button
                Width="45"
                Height="45"
                Style="{DynamicResource MaterialDesignFloatingActionAccentButton}" 
                Grid.Column="1" 
                Grid.Row="1"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Margin="0 10 0 0">
                <Viewbox Width="24" Height="24">
                    <Canvas Width="24" Height="24">
                        <Path Data="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" Fill="White" />
                    </Canvas>
                </Viewbox>
            </Button>
        </Grid>
    </materialDesign:DialogHost>
</UserControl>

DataContext 视图模型:

The DataContext ViewModel:

public class CountryViewModel : BaseViewModel
    {
        private ObservableCollection<CountryItem> countries;
        private DelegateCommand<ItemsControl> loadDataCommand;

        public CountryViewModel ( )
        {
            ViewModelFinder.Add(this);
        }

        public ObservableCollection<CountryItem> Countries
        {
            get
            {
                return countries;
            }

            set
            {
                this.countries = value;
                this.NotifyPropertyChanged();
            }
        }

        public DelegateCommand<ItemsControl> LoadDataCommand
        {
            get
            {
                if ( this.loadDataCommand == null )
                    this.loadDataCommand = new DelegateCommand<ItemsControl>(async icMain => await LoadDataMethod(icMain));

                return this.loadDataCommand;
            }
        }

        private async Task LoadDataMethod (ItemsControl icMain)
        {
            if ( this.Countries == null )
            {
                var countries = await CountryService.Instance.GetAllAsync();
                this.Countries = new ObservableCollection<CountryItem>();
                var util = new Util();
                foreach ( var country in countries.Take(40) )
                {
                    var countryItem = new CountryItem
                    {
                        Name = country.Name,
                        Iso2 = country.Iso2,
                        IsoAlpha3 = country.IsoAlpha3,
                        IsoUnM49Numerical = country.IsoUnM49Numerical,
                        Id = country.Id
                    };
                    var imageBytes = await util.GetImageBytesAsync(country.CountryFlagUrl);
                    countryItem.ImageUrl = country.CountryFlagUrl;
                    this.Countries.Add(countryItem);
                    countryItem.This = icMain.ItemContainerGenerator.ContainerFromItem(countryItem) as UIElement;
                }
            }
        }
    }

表示包装面板中项目的类:

Class that represents the item in a wrappanel:

public class CountryItem : ObservableObject
    {
        private bool isEnabled = true;
        private bool isInEditMode;
        private bool isVisible = true;
        private DelegateCommand isInEditModeToggleCommand;
        private string name;
        private string iso2;
        private string isoAlpha3;
        private int isoUnM49Numerical;
        private string imageUrl;
        private Guid id;
        private UIElement @this;

        public CountryItem ( )
        {

        }

        public bool IsEnabled
        {
            get
            {
                return isEnabled;
            }

            set
            {
                this.isEnabled = value;
                this.NotifyPropertyChanged();
            }
        }
        [Required(AllowEmptyStrings = false, ErrorMessage = "Name is required.")]
        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
                this.NotifyPropertyChanged();
            }
        }
        [Required(AllowEmptyStrings = false, ErrorMessage = "Iso2 is required.")]
        public string Iso2
        {
            get
            {
                return this.iso2;
            }
            set
            {
                this.iso2 = value;
                this.NotifyPropertyChanged();
            }
        }
        [Required(AllowEmptyStrings = false, ErrorMessage = "Iso Alpha3 is required.")]
        public string IsoAlpha3
        {
            get
            {
                return this.isoAlpha3;
            }
            set
            {
                this.isoAlpha3 = value;
                this.NotifyPropertyChanged();
            }
        }
        [Required(AllowEmptyStrings = false, ErrorMessage = "Iso Un M49 Numerical is required.")]
        public int IsoUnM49Numerical
        {
            get
            {
                return this.isoUnM49Numerical;
            }
            set
            {
                this.isoUnM49Numerical = value;
                this.NotifyPropertyChanged();
            }
        }

        public string ImageUrl
        {
            get
            {
                return imageUrl;
            }

            set
            {
                this.imageUrl = value;
                this.NotifyPropertyChanged();
            }
        }

        public Guid Id
        {
            get
            {
                return id;
            }
            set
            {
                this.id = value;
            }
        }

        public bool IsInEditMode
        {
            get
            {
                return isInEditMode;
            }

            set
            {
                this.isInEditMode = value;
                this.NotifyPropertyChanged();
            }
        }

        public DelegateCommand IsInEditModeToggleCommand
        {
            get
            {
                if ( this.isInEditModeToggleCommand == null )
                    this.isInEditModeToggleCommand = new DelegateCommand(IsInEditModeToggleMethod);

                return this.isInEditModeToggleCommand;
            }
        }

        public bool IsVisible
        {
            get
            {
                return isVisible;
            }

            set
            {
                this.isVisible = value;
                this.NotifyPropertyChanged();
            }
        }

        public UIElement This
        {
            get
            {
                return this.@this;
            }

            set
            {
                this.@this = value;
                this.NotifyPropertyChanged();
            }
        }

        private void IsInEditModeToggleMethod ( )
        {
            var countryViewModel = ViewModelFinder.FindOne<CountryViewModel>();
            countryViewModel.Countries.Where(x=>x.Id != this.Id).ToList().ForEach(ci => ci.IsVisible = false);
            this.This.TranslatePoint(new Point(400, 500), Application.Current.MainWindow)
            this.IsInEditMode = !this.IsInEditMode;
        }
    }

推荐答案

您可以使用 UIElement.TranslatePoint 函数.那么您只需要一种在动画中使用转换值的方法,应该有多种方法可以实现这一点.

You can translate coordinates with UIElement.TranslatePoint function. Then you just need a way to use the translated value in your animation, there should be multiple ways to accomplish this.

一种选择是,在集合之外拥有一个专用的 CurrentEditItem,并根据是否设置了此属性来修改窗口内容模板.这样,窗口和项目之间的关系应该更容易建模.但我确信也可以在 To 值上抛出一个绑定和转换器,以便让它们转换窗口到项目的坐标值.

One option would be, to have a dedicated CurrentEditItem outside the collection and modify the window content template based on, whether this property is set. This way, the relation between window and item should be easier to model. But I'm sure its also possible to just throw a binding and converter on your To values, in order to have them translate window-to-item coordinate values.

例如,在您的窗口上实例化为静态资源并引用窗口和带有您的项目的 ConverterParameter 的转换器将允许您在窗口和项目之间转换任何传入值坐标.

For example, a converter that is instantiated as a static resource on your window with a reference to the window and a ConverterParameter with your item would allow you, to convert any incomming value between window and item coordinates.

这篇关于C# 设置相对于 MainWindow 的 translatetransform X 和 Y 属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆