none
如何在一些有Items的控件里随意放置Item? RRS feed

  • 问题

  • 我有个困惑,以一个listbox为例,我想绑定一个list集合,然后里面的Item的位置我想随机生成,但是发觉items的panel只支持竖排或者横排,没有其他方式。请问有什么办法能够设置item的位置。谢谢。
    2011年4月29日 3:57

答案

  • 你好,

    感谢你给的建议,把panel改成Canvas,但是我发现有个问题在里面,就是一旦指定了item的top和left位置,就会发现所有的item全重合在一起了,我想问一下有没有什么办法可以随机生成Item的位置?

    以下是我的代码:

    xaml:

      <Window.Resources>
            <CommonDialog:AngleService x:Key="AngleServiceInstance" />
            <ObjectDataProvider  x:Key="AngleProvider" ObjectInstance="{StaticResource AngleServiceInstance}" x:Shared="False"  MethodName="GetAngle" />
            <ObjectDataProvider  x:Key="TopOffsetProvider" ObjectInstance="{StaticResource AngleServiceInstance}" x:Shared="False"  MethodName="TopOffset" />
        </Window.Resources>
        <Grid>
            <ListBox Name="bbbb" ItemsSource="{Binding}">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas Height="300" Width="300" />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ListBoxItem}">
                        <Setter Property="Canvas.Top" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="Canvas.Left" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="RenderTransform">
                            <Setter.Value>
                                <RotateTransform Angle="{Binding Source={StaticResource AngleProvider}}"/>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.ItemContainerStyle>
                <ListBox.ItemTemplate>
                    <DataTemplate DataType="{x:Type Air:Photo}">
                        <Border BorderBrush="Gray" BorderThickness="4" Height="60" Width="60" >
                            <Image Source="{Binding Location}" />
                        </Border>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

        </Grid>
    </Window>

    后面cs代码:

        public class AngleService
        {
            private static Random rand = new Random();
            public int GetAngle()
            {
                return rand.Next(0, 90);
            }

            public int TopOffset()
            {
                return rand.Next(0, 300);
            }
        }

    一种想法..没有测试

    在ItemsContainerStyle的Setter集合中添加一个EventSetter 每次Load的时候使用随机函数生成Left和Top偏移..

    <EventSetter Event="Loaded" Handler="OnLoaded" />

    后置代码:

     

            private void OnLoaded(object sender, RoutedEventArgs e)

            {

                var listBoxItem = sender as ListBoxItem;

                if (null != listBoxItem)

                {

                    double left = new Random().NextDouble() * 100;

                    double top = new Random().NextDouble() * 100;

                    Canvas.SetLeft(listBoxItem, Left);

                    Canvas.SetTop(listBoxItem, top);

                }

            }

     


    just another day.
    2011年5月3日 15:23

全部回复

  • 这个我推荐你定义一个自定义的Panel,这个panel可以随机Arrange子控件的位置,然后应用这个panel到ListBox的ItemsPanel上,关于如何定义Custom Panel你可以参考:

    http://www.codeproject.com/Articles/37348/Creating-Custom-Panels-In-WPF.aspx

    http://wpftutorial.net/CustomLayoutPanel.html

    http://www.switchonthecode.com/tutorials/wpf-tutorial-creating-a-custom-panel-control

     如果有疑问,请告诉我

     


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    2011年4月29日 7:09
    版主
  • 看一下这个 http://social.msdn.microsoft.com/Forums/en/wpf/thread/67ab19bc-ddde-4991-9ee3-dce41d2ecb35

    在一个ItemsControl里面用Canvas作为他的ItemsPanel就可以了,然后你只要设置ItemContainerStyle,在里面指定 Canvas的几个位置附加属性 就可以对这个Item进行定位了。

     

    J


    Best day, Best life
    2011年4月29日 18:12
  • 感谢Jarrey的精彩补充,开始我也想到用Canvas,这样的话会简洁些,但是随即位置这里还要用绑定去安排位置。

    所以我在上个reply中用了 “我推荐”的方法,这样的panel做随机会方便点,让王晶晶自己选择适合他的吧。


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年4月30日 4:45
    版主
  • 你好,

    感谢你给的建议,把panel改成Canvas,但是我发现有个问题在里面,就是一旦指定了item的top和left位置,就会发现所有的item全重合在一起了,我想问一下有没有什么办法可以随机生成Item的位置?

    以下是我的代码:

    xaml:

      <Window.Resources>
            <CommonDialog:AngleService x:Key="AngleServiceInstance" />
            <ObjectDataProvider  x:Key="AngleProvider" ObjectInstance="{StaticResource AngleServiceInstance}" x:Shared="False"  MethodName="GetAngle" />
            <ObjectDataProvider  x:Key="TopOffsetProvider" ObjectInstance="{StaticResource AngleServiceInstance}" x:Shared="False"  MethodName="TopOffset" />
        </Window.Resources>
        <Grid>
            <ListBox Name="bbbb" ItemsSource="{Binding}">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas Height="300" Width="300" />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ListBoxItem}">
                        <Setter Property="Canvas.Top" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="Canvas.Left" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="RenderTransform">
                            <Setter.Value>
                                <RotateTransform Angle="{Binding Source={StaticResource AngleProvider}}"/>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.ItemContainerStyle>
                <ListBox.ItemTemplate>
                    <DataTemplate DataType="{x:Type Air:Photo}">
                        <Border BorderBrush="Gray" BorderThickness="4" Height="60" Width="60" >
                            <Image Source="{Binding Location}" />
                        </Border>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

        </Grid>
    </Window>

    后面cs代码:

        public class AngleService
        {
            private static Random rand = new Random();
            public int GetAngle()
            {
                return rand.Next(0, 90);
            }

            public int TopOffset()
            {
                return rand.Next(0, 300);
            }
        }

    2011年5月3日 14:38
  • 我想所有的Item元素都随机生成位置,所以我感觉不能在Item style里面去定义随机数,只是我不知道怎么改进,请问有什么办法?谢谢
    2011年5月3日 14:44
  • 我试了一下Jarrey的建议,但是发现一个问题,问题我已经在上面描述了,又试了一下你的建议重写panel,但是我感觉还是有另外的一个问题,就是我在里面取随机数的时候我需要知道panel的实际宽和高(因为我想把Item的随机位置控制在panel内部),但现在貌似在重写的时候获取不到。我不清楚.net自带的盘饿了怎么保证item一定处在panel内部的。还有另外的一个问题,就是最后的一个item,他会占剩下的所有panel空间,我怎么样才能改掉不让他所有的空间,谢谢?

    代码如下:

    public class ScatterPanel : Panel
        {
            protected override Size MeasureOverride(Size availableSize)
            {
                foreach (UIElement child in Children)
                    child.Measure(availableSize));

                // Creating random positions and scales
                foreach (UIElement child in Children)
                {
                    SetAngle(child, Randoms.Angle);
                    SetOffsetX(child, Randoms.OffsetX);
                    SetOffsetY(child, Randoms.OffsetY);
                    SetScale(child, Randoms.Scale);
                    child.RenderTransformOrigin = new Point(.5, .5);
                }

                return base.MeasureOverride(availableSize);
            }

            protected override Size ArrangeOverride(Size finalSize)
            {
                foreach (UIElement child in Children)
                {
                    child.Arrange(new Rect(0, 0, child.DesiredSize.Width, child.DesiredSize.Height));

                    var group = new TransformGroup();
                    group.Children.Add(new RotateTransform(GetAngle(child)));
                    group.Children.Add(new ScaleTransform(GetScale(child), GetScale(child)));
                    group.Children.Add(new TranslateTransform(GetOffsetX(child), GetOffsetY(child)));
                    child.RenderTransform = group;
                }

                return base.ArrangeOverride(finalSize);
            }

            public int Angle
            {
                get { return (int)GetValue(AngleProperty); }
                set { SetValue(AngleProperty, value); }
            }
            public static readonly DependencyProperty AngleProperty = DependencyProperty.RegisterAttached("Angle", typeof(int), typeof(ScatterPanel), new UIPropertyMetadata(0));
            public static void SetAngle(DependencyObject target, int value) { target.SetValue(AngleProperty, value); }
            public static int GetAngle(DependencyObject target) { return (int)target.GetValue(AngleProperty); }

            public double Scale
            {
                get { return (double)GetValue(ScaleProperty); }
                set { SetValue(ScaleProperty, value); }
            }
            public static readonly DependencyProperty ScaleProperty = DependencyProperty.RegisterAttached("Scale", typeof(double), typeof(ScatterPanel), new UIPropertyMetadata(0d));
            public static void SetScale(DependencyObject target, double value) { target.SetValue(ScaleProperty, value); }
            public static double GetScale(DependencyObject target) { return (double)target.GetValue(ScaleProperty); }

            public double OffsetX
            {
                get { return (double)GetValue(OffsetXProperty); }
                set { SetValue(OffsetXProperty, value); }
            }
            public static readonly DependencyProperty OffsetXProperty = DependencyProperty.RegisterAttached("OffsetX", typeof(double), typeof(ScatterPanel), new UIPropertyMetadata(0d));
            public static void SetOffsetX(DependencyObject target, double value) { target.SetValue(OffsetXProperty, value); }
            public static double GetOffsetX(DependencyObject target) { return (double)target.GetValue(OffsetXProperty); }

            public double OffsetY
            {
                get { return (double)GetValue(OffsetYProperty); }
                set { SetValue(OffsetYProperty, value); }
            }
            public static readonly DependencyProperty OffsetYProperty = DependencyProperty.RegisterAttached("OffsetY", typeof(double), typeof(ScatterPanel), new UIPropertyMetadata(0d));
            public static void SetOffsetY(DependencyObject target, double value) { target.SetValue(OffsetYProperty, value); }
            public static double GetOffsetY(DependencyObject target) { return (double)target.GetValue(OffsetYProperty); }
        }

    -------------------------------------------------------------------------------------

    这里面的随机数都是硬编码,因为我现在没法获得panel的实际大小,所以有些随机数生成后会是item处在panel的外面

     public static class Randoms
        {
            private static readonly Random _random = new Random(Environment.TickCount);

            public static decimal Rating
            {
                get
                {
                    int number = _random.Next(10, 50);
                    return ((decimal)number) / 10;
                }
            }  

            public static Point Coordinates
            {
                get
                {
                    int x = _random.Next(150, 1100);
                    int y = _random.Next(100, 700);
                    return new Point(x, y);
                }
            }

            public static int Angle
            {
                get
                {
                    return _random.Next(10, 350);
                }
            }

            public static double Scale
            {
                get
                {
                    return (double)_random.Next(50, 110)/100;
                }
            }

            public static double OffsetX
            {
                get
                {
                    return _random.Next(0, 750);
                }
            }

            public static double OffsetY
            {
                get
                {
                    return _random.Next(0, 500);
                }
            }
        }

     

    请问一下有没有好一点的办法改进我上面提出的两个问题,谢谢!

    2011年5月3日 14:58
  • 你好,

    感谢你给的建议,把panel改成Canvas,但是我发现有个问题在里面,就是一旦指定了item的top和left位置,就会发现所有的item全重合在一起了,我想问一下有没有什么办法可以随机生成Item的位置?

    以下是我的代码:

    xaml:

      <Window.Resources>
            <CommonDialog:AngleService x:Key="AngleServiceInstance" />
            <ObjectDataProvider  x:Key="AngleProvider" ObjectInstance="{StaticResource AngleServiceInstance}" x:Shared="False"  MethodName="GetAngle" />
            <ObjectDataProvider  x:Key="TopOffsetProvider" ObjectInstance="{StaticResource AngleServiceInstance}" x:Shared="False"  MethodName="TopOffset" />
        </Window.Resources>
        <Grid>
            <ListBox Name="bbbb" ItemsSource="{Binding}">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas Height="300" Width="300" />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ListBoxItem}">
                        <Setter Property="Canvas.Top" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="Canvas.Left" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="RenderTransform">
                            <Setter.Value>
                                <RotateTransform Angle="{Binding Source={StaticResource AngleProvider}}"/>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.ItemContainerStyle>
                <ListBox.ItemTemplate>
                    <DataTemplate DataType="{x:Type Air:Photo}">
                        <Border BorderBrush="Gray" BorderThickness="4" Height="60" Width="60" >
                            <Image Source="{Binding Location}" />
                        </Border>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

        </Grid>
    </Window>

    后面cs代码:

        public class AngleService
        {
            private static Random rand = new Random();
            public int GetAngle()
            {
                return rand.Next(0, 90);
            }

            public int TopOffset()
            {
                return rand.Next(0, 300);
            }
        }

    一种想法..没有测试

    在ItemsContainerStyle的Setter集合中添加一个EventSetter 每次Load的时候使用随机函数生成Left和Top偏移..

    <EventSetter Event="Loaded" Handler="OnLoaded" />

    后置代码:

     

            private void OnLoaded(object sender, RoutedEventArgs e)

            {

                var listBoxItem = sender as ListBoxItem;

                if (null != listBoxItem)

                {

                    double left = new Random().NextDouble() * 100;

                    double top = new Random().NextDouble() * 100;

                    Canvas.SetLeft(listBoxItem, Left);

                    Canvas.SetTop(listBoxItem, top);

                }

            }

     


    just another day.
    2011年5月3日 15:23
  • 刚洗漱回来, 看到你的回复想针对你的代码给你点建议。

     <ListBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ListBoxItem}">

                        <Setter Property="Canvas.Top" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="Canvas.Left" Value="{Binding Source={StaticResource TopOffsetProvider}}"/>
                        <Setter Property="RenderTransform">
                            <Setter.Value>
                                <RotateTransform Angle="{Binding Source={StaticResource AngleProvider}}"/>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.ItemContainerStyle>

    这部分,你可以这样做

     <ListBox.ItemContainerStyle>

        <Style TargetType="{x:Type ContentPresenter}">

          <Setter Property="Canvas.Left" Value="{Binding Path=. , Converter={StaticResource itemsConvert}}" />

          <Setter Property="Canvas.Top" Value="{Binding Path=. , Converter={StaticResource itemsConvert}}" />

         </Style>

      </ListBox.ItemContainerStyle>

    然后在这个Convert里面实现随机的位置, 这样应该可以~


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年5月3日 16:27
    版主
  • Hi Sheldon,

    我试了一下你的建议,但是有这样的一个问题:

    用于类型“ContentPresenter”的样式不能应用于类型“ListBoxItem”。

     请问你说的那个方法怎么实现的,能不能给段实例代码,谢谢。

     

    Thanks

    Hubert

    2011年5月4日 5:07
  • 这种方法听上去很可行,我试试,不过貌似速度应该很慢。谢谢!
    2011年5月4日 5:18
  • 你好,我试了一下,是可以实现的,不过需要修改下:

    private static Random rand = new Random();

      private void OnLoaded(object sender, RoutedEventArgs e)
     
            {
     
                var listBoxItem = sender as ListBoxItem;
     
                if (null != listBoxItem)
     
                {
     
                    double left = rand。 NextDouble() * 100;
     
                    double top = rand。NextDouble() * 100;
     
                    Canvas.SetLeft(listBoxItem, Left);
     
                    Canvas.SetTop(listBoxItem, top);
     
                }
     
            }

    2011年5月4日 5:27
  • 但是还有一个问题,在里面我怎么获取那个ListBox里面的那个Canvas的实际大小啊,因为我想把随机数定在那个canvas内部?
    2011年5月4日 5:30
  • 不好意思,还有个后续问题。当我的窗体size改变后,ListBox的Size也对应产生变化,这是我想是ListBoxItem的位置也相应的变化,但目前的的ListBoxItem相对Canvas的位置都是钉死的,请问怎么把Items的Location位置重新设置一下,谢谢!
    2011年5月4日 5:43