This is a migrated thread and some comments may be shown as answers.

Drag & drop questions

13 Answers 223 Views
TreeListView
This is a migrated thread and some comments may be shown as answers.
Goran
Top achievements
Rank 1
Goran asked on 25 Apr 2012, 02:37 AM
Hi,

I have some questions regarding the Drag & Drop operation. I would like to achieve this using DragDropManager.

1) How can I start the drag operation, while providing the "No drop allowed" sign over the control that started the drag? Basically, I just want to allow only dragging and deny droppping to a particular RadTreeListView control. I am guessing that DragDropBehavior's CanDrop method could be a solution, but I am not sure how to do this. An example would be nice.

2) Similar to above question, how can I provide the "No drop allowed" sign if dragged data is not of particular type?

3) How can I enable scrolling during DragDrop operation? I found an example that uses RadDragAndDropManager, but could not find an example that uses DragDropManager.

Thanks in advance.

13 Answers, 1 is accepted

Sort by
0
Nick
Telerik team
answered on 26 Apr 2012, 01:31 PM
Hello Goran,

We are currently working on more examples and documentation for the new Drag & Drop manager, and we will update the existing with the release of Q2 2012. 

Manipulating the sign in the DragDropManager can be achieved by modifying the DragDropEffects of the current drag operation. You can achieve the functionality in your second question by using the DragOver event. 
As for the other two questions, I am attaching an example that demonstrates a simple approach on how to achieve the functionality in question 1. You can implement a custom AutoScrollBehavior similar to the one in this blog post. There are few changes that has to be made in order to adapt it for the DragDropManager, but the Idea is more or less the same. 

public class AutoScrollBehavior : Behavior<RadGridView>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.RowLoaded += AssociatedObject_RowLoaded;
        }
 
        private bool isAttached = false;
        void AssociatedObject_RowLoaded(object sender, RowLoadedEventArgs e)
        {
            if (!this.isAttached)
            {
                try
                {
                    var scrollViewer = this.AssociatedObject.ChildrenOfType<GridViewScrollViewer>().FirstOrDefault();
                    DragDropManager.AddDragOverHandler(scrollViewer, OnGridViewDragOver, true);
                    this.isAttached = true;
                }
                catch { }
            }
        }
 
        protected override void OnDetaching()
        {
            base.OnDetaching();
 
            try
            {
                DragDropManager.RemoveDragOverHandler(this.AssociatedObject.ChildrenOfType<GridViewScrollViewer>().FirstOrDefault(), OnGridViewDragOver);
            }
            catch { }
        }
 
        private void OnGridViewDragOver(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
        {
            var scrollViewer = sender as GridViewScrollViewer;
 
            if (scrollViewer != null)
            {
 
                var currentDragPoint = e.GetPosition(Application.Current.RootVisual);
 
                var generalTransform = scrollViewer.TransformToVisual(Application.Current.RootVisual);
 
                var topLeft = generalTransform.Transform(new Point(0, 0));
                var relative = new Point(currentDragPoint.X - topLeft.X, currentDragPoint.Y - topLeft.Y);
 
                if (relative.Y > 0 && relative.Y < 40)
                {
                    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - (20 * ((40 - relative.Y) / 40)));
                }
 
                if (relative.Y > scrollViewer.ActualHeight - 40 && relative.Y < scrollViewer.ActualHeight)
                {
                    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + (20 * ((40 - (scrollViewer.ActualHeight - relative.Y)) / 40)));
                }
 
                if (relative.X > 0 && relative.X < 40)
                {
                    scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - (20 * ((40 - relative.X) / 40)));
                }
 
                if (relative.X > scrollViewer.ActualWidth - 40 && relative.X < scrollViewer.ActualWidth)
                {
                    scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + (20 * ((40 - (scrollViewer.ActualWidth - relative.X)) / 40)));
                }
            }
        }
    }

Hope this helps! 

All the best,
Nik
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Goran
Top achievements
Rank 1
answered on 26 Apr 2012, 03:10 PM
Hi Nik,

thanks for the example with auto scrolling. I also needed to modify it for the RadTreeView control, and since there is no RowLoaded event, I used ItemPrepared event (not too much choices there), and also make change GridViewScrollViewer -> ScrollViewer. Is it a proper way to achieve the same result with RadTreeView?

Considering the documentation for DragDropManager, it seems that the  main problem you are having with documentation in general is lack of information. For example, you will create some new examples, which would cover approx 20% of possible situations, and if there is no proper explanations what each method/event/property does, then you will still have tons of questions in the forum.

Lets take a TreeListView as example, There is a IsDragDropEnabled property which you do not use in your example. Documentation says only this: Gets or sets a value indicating whether drag and drop is enabled. What I really dont know is what does it actually do? Does it use AddDragInitializeHandler internally? It seems that you should not use them both at the same time (IsDragDropEnabled and AddDragInitializeHandler). What is the difference then? This things are confusing. Then, you are using AllowCapturedDrag property. Documentation says: Gets a value indicating whether this element can raise DragInitialize event and be used as the source of a drag-and-drop operation. And then you also set AllowDrag (???). Documentation: Sets the AllowDrag attached property. Not too much info here. Why do I need to set both? If I am allowed to raise DragInitialize with AllowCapturedDrag = true, then it is natural to assume I will be doing some dragging, right?

And there are many, many examples like this. If you want us to think with our heads, you need to provide us with some proper information in documentation, with a very small example if neccessery. Currently I can just blindly take what you have given and use it in my project, and if I have a problem, I need support again.

This is just a friendly advice, don't take me wrong, it seems that you have taken completely wrong path with documentation, which has resulted in lots of bloated information in forums, and lots of missing information.

Back to the "no drag" visual effect. I have also tried something similar, but the problem with this approach is that I am loosing the "inserting line" visual effect, which is shown by default during drag operation on RadTreeListView and RadTreeView. This visual effect is the primary reason why I am using RadTreeListView instead of RadGridView.

So, question would be how can we do this in OnDragOver:

1) if item is of typeA, then user can drop item, so I would like "inserting line" to be displayed, as is the default behavior when you set IsDragDropEnabled on RadTreeListVIew
2) if item is not of TypeA, then display "no drop allowed" visual cue, and do not display the "inserted line" since dropping is not allowed.

Thanks,
Goran
0
Nick
Telerik team
answered on 30 Apr 2012, 12:12 PM
Hi Goran,

We understand your frustration, and we are doing our best to add more detailed help articles, documentation and examples. 

if item is not of TypeA, then display "no drop allowed" visual cue, and do not display the "inserted line" since dropping is not allowed. 
You can do this by setting the effects of the current drag operation to none. 

if item is of typeA, then user can drop item, so I would like "inserting line" to be displayed, as is the default behavior when you set IsDragDropEnabled on RadTreeListVIew 
Unfortunately, by default the TreeList does not offer this behavior outside of its internal Drag and Drop implementation. You can create a custom drop indication, by various ways, one of which is creating custom Visual States of the TreeList rows and change them in the DragOver, DragEnter and DragLeave events. Another approach could be to create a popup indicator in the position that an item would be added after drop, etc.

Hope this helps! 

All the best,
Nik
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Goran
Top achievements
Rank 1
answered on 30 Apr 2012, 12:47 PM
Hi, Nik

do you have any advice or share the code / template for tweak the internal mechanism, so I would not loose a lot of time looking at the Telerik source code. My preferred option would be to subclass the control and override some base methods, since if I need to change the source, then I would need to change it in each Telerik release in the future. Any info in this direction is appreciated.

What I basically need is to provide some (new) event, which will not disturb the current inside drag&drop implementation, where I could just check the type, and deny the drop if necessary, based on the data type that is being dragged.
0
Nick
Telerik team
answered on 02 May 2012, 03:28 PM
Hi Goran, 

What I basically need is to provide some (new) event, which will not disturb the current inside drag&drop implementation, where I could just check the type, and deny the drop if necessary, based on the data type that is being dragged. 
If you need just this, for the time being you can use the DropQuery event of our old Drag & Drop mechanism. If there is no additional scenario details, this should be sufficient enough since there is "mapping" between the old and the new managers events. 

As for the way we implemented it. We use an element predefined in the TreeList template. To be more precise: it is a Grid named "DragBetweenItemsFeedback". We place it on its appropriate place in the DragInfo event handler. However, I would advise you however to create some custom visual states for the TreeListRow and use them to indicate the DropPosition.

Hope this helps! 

Greetings,
Nik
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Goran
Top achievements
Rank 1
answered on 25 May 2012, 10:46 AM
Hi, Nik,

I already knew that I can do this with DragAndDropManager, but my question was related to DragDropManager, since RadDragAndDropManager will be obsoleted in next relesase, as I was informed.

Are you saying that this cannot be done with new manager?

Regards,
Goran
0
Nick
Telerik team
answered on 29 May 2012, 01:58 PM
Hello Goran,

You currently can use the Internal implementation of the TreeList drag and drop, which will provide you the drop indicator line, and combine that with the DragOver event of the DragDropManager to manage, whether the drop is permitted or not. 

public MainPage()
{
    InitializeComponent();
 
    this.TreeList.PreviewDragEnded += TreeList_PreviewDragEnded;
    DragDropManager.AddDropHandler(this.TreeList, OnDrop, true);
    DragDropManager.AddDragOverHandler(this.TreeList, OnDragOver, true);
}
 
void TreeList_PreviewDragEnded(object sender, Telerik.Windows.Controls.RadTreeListViewDragEndedEventArgs e)
{
    Debug.WriteLine("Internal DragEnded");
}
 
void OnDrop(object sender, DragEventArgs e)
{
    Debug.WriteLine("DDM drop");
}
 
void OnDragOver(object sender, DragEventArgs e)
{
    Debug.WriteLine("Dragging Over the TreeList");
    e.Effects = System.Windows.DragDropEffects.None;
    e.Handled = true;
}

Hope this helps!  Greetings,
Nik
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Goran
Top achievements
Rank 1
answered on 29 May 2012, 11:33 PM
Hi, Nik,

If you look at my question, your example doesn't achieve anything. It only makes life easier to programmer, and makes sure DropEvent would not fire. My goal is to provide "No Drop" allowed icon using DragDropManager, so I can notify user that drop is not allowed. Let me repeat the conditions:

- There are several TreeListView controls on the View
- TreeListVIewA can order items inside itself, for that purpose I am using built in drop line indicator, which provides good user experience.
- TreeListViewB can NOT drop items in the TreeListVIewA, but can drop items into TreeListVIewB.

So, when user starts dragging an item from TreeListViewB, user must be visually notified, when over TreeListViewA that no drop is allowed. Your example doesn't provide resolution to this problem.

Regards,
Goran
0
Nick
Telerik team
answered on 31 May 2012, 02:24 PM
Hello Goran,

You can cache the DragVisual in the DragInitialized event (has to be subscribed with handled events too set to true), and then change its visual states manually in the DragOver event handler. The two states are "DropPossible" and "DropImpossible". 
Usually the DragVisual should react to the changes in the DragEffects and the Query result, however it seems that the TreeListDragCuew does not react to those changes. We are currently investigating the problem and will try to fix it as soon as possible.

Hope this helps! 

Greetings,
Nik
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Goran
Top achievements
Rank 1
answered on 22 Jun 2012, 07:49 PM
Hi, Nik,

any news about the solution? It is kind of important to me to resolve this issue before a project goes to testing.

Regards,
Goran
0
Nick
Telerik team
answered on 25 Jun 2012, 05:27 AM
Hi Goran,

The issue with the TreeListDragVisualCue has been resolved and will be available for download in our next internal build.

We are sorry for any inconvenience caused! 

Kind regards,
Nik
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Senthil kumar
Top achievements
Rank 1
answered on 04 Oct 2013, 01:28 PM
currently, i am using on telerik RAD control version 2013.2.724.40. my assignment is to implement the dragdrop from RADGridview to itemcontrol. The itemscontrol contains a RADCartesianChart and when dropping a row from datagrid to itemcontrol to show the new chart for the related selected row.
suppose, when dropping the selected row item in existing RADCartesianChart, then new lineseries and y-Axis (multiple Y-axis) to be created in same chart. (it will share same X-axis). if user drops the selected row out side the RADCartesianChart (but, within the itemscontrol), then created new chart with in the item control. 

i have created ItemControlDragDropBehaviour as same as suggested in telerik's example. so, it works fine with itemscontrol. but, when i dropping the selected item with in existing chart in the itemscontros, it is not give the dropped chart control.

how to find the chart control when dropping in exact chart control when dropping over the chart control in the itemscontrol.


Code:

<telerik:RadGridView Grid.RowSpan="2" Width="280" HorizontalContentAlignment="Stretch" VerticalAlignment="Stretch"
                             Name="radGridView1" 
                             ItemsSource="{Binding  Path=ChannelList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, IsAsync=True}"
                             AutoGenerateColumns="False" 
                            AllowDrop="True"
                             demo:GridViewDragDropBehavior.IsEnabled="True"
                             SelectionMode="Single"
                             IsReadOnly="True"
                             IsSynchronizedWithCurrentItem="True"
                             SelectedItem="{Binding Path=SelectedChannel, IsAsync=True,UpdateSourceTrigger=PropertyChanged}"
                                 IsFilteringAllowed="True"
                                 ShowInsertRow="False"                                 
                                 CanUserDeleteRows="False"                                 
                                 CanUserInsertRows="False"
                                 CanUserReorderColumns="False"
                                 GridLinesVisibility="Horizontal"
                                 RowIndicatorVisibility="Collapsed" Margin="0,0,70,0">
            <telerik:RadGridView.Resources>
                <DataTemplate x:Key="DraggedItemTemplate">
                    <StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Dragging:"/>
                            <TextBlock Text="{Binding CurrentDraggedItem.Name}" FontWeight="Bold"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding CurrentDropPosition}" FontWeight="Bold" MinWidth="45"/>
                            <TextBlock Text=", (" Foreground="Gray" />
                            <TextBlock Text="{Binding CurrentDraggedOverItem.Name}"/>
                            <TextBlock Text=")" Foreground="Gray" />
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </telerik:RadGridView.Resources>
            <telerik:RadContextMenu.ContextMenu>
                <telerik:RadContextMenu x:Name="radGridContextMenu" ItemsSource="{Binding SubMenus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
                </telerik:RadContextMenu>
            </telerik:RadContextMenu.ContextMenu>

            <telerik:RadGridView.RowStyle>
                <Style TargetType="telerik:GridViewRow">
                    <Setter Property="telerik:DragDropManager.AllowDrag"
                           Value="True" />
                    <Setter Property="telerik:DragDropManager.TouchDragTrigger"
                           Value="TapAndHold"/>
                </Style>
            </telerik:RadGridView.RowStyle>

            
            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn Header="Subsystem" IsSortable="True" IsFilterable="True" IsVisible="False" IsGroupable="True" Width="*" DataMemberBinding="{Binding SubsystemText}"/>
                <telerik:GridViewDataColumn Header="Show" Width="30">
                    <telerik:GridViewDataColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsThreeState="False" IsChecked="{Binding Selected, Mode=TwoWay}" FlowDirection="RightToLeft" />
                        </DataTemplate>
                    </telerik:GridViewDataColumn.CellTemplate>
                </telerik:GridViewDataColumn>
                <telerik:GridViewDataColumn Header="ID" IsSortable="True" IsFilterable="True" IsGroupable="True" Width="50" DataMemberBinding="{Binding SubsystemId}"/>
                <telerik:GridViewDataColumn Header="Signal" IsSortable="True" IsFilterable="True" IsGroupable="True" Width="*" DataMemberBinding="{Binding Name}">
                    <telerik:GridViewDataColumn.ToolTipTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}"/>
                        </DataTemplate>
                    </telerik:GridViewDataColumn.ToolTipTemplate>
                </telerik:GridViewDataColumn>
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>
      
        <ScrollViewer  Margin="2" VerticalAlignment="Stretch"  CanContentScroll="True" Grid.Column="1" Grid.Row="1">
            <ItemsControl x:Name="chartHost"  BorderThickness="2"  BorderBrush="Black"   VerticalAlignment="Stretch"  demo:ItemControlDragDropBehaviour.IsEnabled="True" AllowDrop="True"  ItemsSource="{Binding SelectedSignals, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical" Margin="2" Background="Chocolate" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.ItemTemplate>
                <DataTemplate>
  <Border BorderThickness="1" BorderBrush="Black" Grid.Row="0">
            <StackPanel Orientation="Vertical">
         <chart:RadCartesianChart x:Name="Chart1"   Height="400" Grid.Row="0" Palette="Windows8" Margin="6,0,20,0" >
            <chart:RadCartesianChart.Resources>
                <DataTemplate x:Key="PointTemplate1">
                    <Ellipse Height="6" Width="6" Fill="#FF8EC441" />
                </DataTemplate>
                <DataTemplate x:Key="PointTemplate2">
                    <Ellipse Height="6" Width="6" Fill="#FF1B9DDE" />
                </DataTemplate>
            </chart:RadCartesianChart.Resources>
            <chart:RadCartesianChart.HorizontalAxis>
                <chartView:DateTimeContinuousAxis PlotMode="OnTicks" LabelFitMode="Rotate" LabelRotationAngle="45"  MajorStepUnit="Second" Minimum="{Binding AxisXMinValue}"
                                                LabelInterval="5"
                                                LabelFormat="hh:mm:ss" />
            </chart:RadCartesianChart.HorizontalAxis>
            <telerik:RadCartesianChart.Grid>
                <telerik:CartesianChartGrid MajorLinesVisibility="Y" StripLinesVisibility="Y" MajorXLineStyle="{StaticResource StripLinesStyle}" MajorYLineStyle="{StaticResource StripLinesStyle}"/>
            </telerik:RadCartesianChart.Grid>
            <chart:RadCartesianChart.VerticalAxis>
                <chartView:LinearAxis />
            </chart:RadCartesianChart.VerticalAxis>
             <demo:LinearSeriesSourceChange.LinearSeriesSource>
                        <Binding Path="ChartList" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"  />
             </demo:LinearSeriesSourceChange.LinearSeriesSource>
            <telerik:RadCartesianChart.Behaviors>
                <telerik:ChartPanAndZoomBehavior ZoomMode="Both" PanMode="Both" >
                </telerik:ChartPanAndZoomBehavior>
            </telerik:RadCartesianChart.Behaviors>
         
        </chart:RadCartesianChart>





 public static class LinearSeriesSourceChange
    {

        public static readonly DependencyProperty LinearSeriesSourceProperty = DependencyProperty.RegisterAttached("LinearSeriesSource",
            typeof(ObservableCollection<HistoricalChannel>), typeof(LinearSeriesSourceChange), new PropertyMetadata(OnLinearSeriesSourceChanged));

        public static ObservableCollection<HistoricalChannel> GetLinearSeriesSource(DependencyObject obj)
        {
            return (ObservableCollection<HistoricalChannel>)obj.GetValue(LinearSeriesSourceProperty);
        }

        public static void SetLinearSeriesSource(DependencyObject obj, ObservableCollection<HistoricalChannel> value)
        {
            obj.SetValue(LinearSeriesSourceProperty, value);
        }

        private static void OnLinearSeriesSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ObservableCollection<string> s = new ObservableCollection<string>();
            s.Add("#8ec441");
            s.Add("#1b9dde");
            s.Add("#f59700");
            s.Add("#d4df32");
        

            RadCartesianChart chart = sender as RadCartesianChart;
            if (chart == null)
                return;
            ObservableCollection<HistoricalChannel> seriesModel = e.NewValue as ObservableCollection<HistoricalChannel>;

            int iCount = 0;
            LinearAxis linearAxis = chart.VerticalAxis as LinearAxis;
            List<CartesianSeries> generatedSeries = new List<CartesianSeries>();
            chart.Series.Clear();
            foreach (HistoricalChannel histriocallist in seriesModel)
            {


                //        CategoricalSeriesDescriptor a = new CategoricalSeriesDescriptor();
                RadObservableCollection<SalesRevenue> list = histriocallist.Data;
                // chart.Series.Clear();

                LineSeries a = new LineSeries();
                a.ItemsSource = list;
               
                LineSeries lineSeries = new LineSeries();
                string pointTemplatePath = string.Format("PointTemplate{0}", iCount + 1);
                lineSeries.PointTemplate = chart.Resources[pointTemplatePath] as DataTemplate;
                
                SeriesLegendSettings legend1 = new SeriesLegendSettings() { Title = "My Title" + iCount.ToString() };
                lineSeries.LegendSettings = legend1;
                lineSeries.CategoryBinding = new PropertyNameDataPointBinding("Timestamp");
                lineSeries.ValueBinding = new PropertyNameDataPointBinding("Revenue");
                lineSeries.ItemsSource = list;

                if (iCount != 0)
                 {
                     linearAxis = new LinearAxis();
                 }
                 if (iCount % 2 == 1)
                 {
                     if (linearAxis != null)
                     {
                         linearAxis.HorizontalLocation = AxisHorizontalLocation.Right;
                         linearAxis.Title = "Tile" + (iCount + 1).ToString();
                         linearAxis.ElementBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(s[iCount]));
                         if (iCount != 0)
                         {
                             lineSeries.VerticalAxis = linearAxis;
                         }
                       
                     }

                 }
                 else
                 {
                     linearAxis = new LinearAxis();
                     linearAxis.HorizontalLocation = AxisHorizontalLocation.Left;
                     
                     linearAxis.Title = "Tile" + (iCount + 1).ToString();
                     linearAxis.ElementBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(s[iCount]));
                     lineSeries.VerticalAxis = linearAxis;
                 }
          
                chart.Series.Add(lineSeries);
                iCount++;
            }
         }
       }
    }
 
0
Ricky Wu
Top achievements
Rank 1
answered on 10 Jan 2014, 09:18 PM
There is now a public ScrollingSettingsBehavior you can use to easily add automatic scrolling during drag operations to TreeListView:

            ScrollingSettingsBehavior.SetIsEnabled(treeListView, true);
            ScrollingSettingsBehavior.SetScrollAreaPadding(treeListView, new Thickness(18.0));
            ScrollingSettingsBehavior.SetScrollStep(treeListView, 24.0);
            ScrollingSettingsBehavior.SetScrollStepTime(treeListView, TimeSpan.FromSeconds(0.05));
Tags
TreeListView
Asked by
Goran
Top achievements
Rank 1
Answers by
Nick
Telerik team
Goran
Top achievements
Rank 1
Senthil kumar
Top achievements
Rank 1
Ricky Wu
Top achievements
Rank 1
Share this question
or