Click here to Skip to main content
15,888,527 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi everyone!

I have uploaded VS code to http://s000.tinyupload.com/index.php?file_id=92033472381453312391[^].
It is simple program to demonstrate how I imagine it would be good practice to move around graphics in WPF.

Here are the questions :
1. As HalfCircleObject grows in functionality, I will eventually want to add new class that extedns HlafCircleObjectMove class which already extends HalfCircleObjectSelect which in turn extends HalfCircleObjectOrigin and so on.
So, to keep using the newest HalfCircleObject I always have to create newest object in button click method. Is there anyway arround this?
For example, my idea would be to create HalfCircleObjectTop class and then do this :
HalfCircleObject->HalfCircleObjectOrigin->HalfCircleObjectSelected->
HalfCircleObjectMove->***->HlafCircleObjectTop

..to put any new class that adds functionality to HalfCircleobject in position *** and never above HalfCircleObjectTop.
That way, in button method and similar ones always use only HalfCircleObjectTop.
(I'm guessing there should be more prudent approach)

2. Please see in static class MoveObject that I am forced to cast ISelect type of Object to IMove type. Is there any to avoid need for casting? (I know that is it mandatory if program stays structured as it is now).
For example :
I imagined that it would be good practice to separate HalfCircleObject by its functionality. So I have written program keeping in mind just that
HlafCircleObjectOrigin should implement only IOrigin and IHit interface,
HlafCircleObjectSelect should implement only ISelect interface,
HlafCircleObjectMove should implement only IMove interface,
and so on.

So I hope I gave you feeling what my problem is and why I can't work around it. It may be that my approach is completely off the tack and that it needs to build from scratch. Any advice is much appreciated.

Thank you!


In short :
C#
class A : UserControl
{

  public A() { InitializeComponent(); }
}

class B : A
{
  Point Origin;
}

class C : B
{
  bool IsSelected {get; set;}
}

class D : C
{
  MoveAround(Point mousePosition)
  {
    Origin = mousePosition;
  }
}

--------------------
//this code is added in future (*)
class E : D
{
  TextBox Tb;
  MoveAroundText(Point mousePosition) { ... }
}

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

static class MoveObject(Point mousePosition)
{
  foreach(D Obj in SomeKindOfList)
    if(Obj.IsSelected) Obj.MoveAround(mousePosition);
}


How do I use MoveAroundText with already developed MoveObject method without changing it?
Posted
Updated 15-Nov-15 22:50pm
v5
Comments
Tomas Takac 16-Nov-15 2:31am    
I think you should split it into multiple questions and provide a proper explanation with (simplified) code samples in each of them. No links please - I won't download your code.
hrvoje59 16-Nov-15 2:59am    
Fair enough. Let me try to ask simpler question then.
Say I created UserControl object (A) in WPF. I set up my graphics in XAML. I wish to extend it by adding more functionality to it. So first I add origin point (B) to it, then ability to be selected (C) and then even ability to be moved around (D). When I create my object I naturally use the latest class (in this example D) wherever I use it. So now when I've written code to deal with class D what happens when I need to add even more functionality to latest class? For example E class should add ToolTip to that UserControl but everywhere in my program I already use D class.

Do I have to change every D object type to E and continue?

If you still find this question unclear I will try to give you specific example.

Thank you for response.
phil.o 16-Nov-15 4:14am    
Please no code in comments. Comments are not meant for that.
Please include this code snippet in your question instead.

First, MoveAround should be a virtual method and you should override it in E, not create a new method with different name:
C#
class D : C
{
  public virtual MoveAround(Point mousePosition) { ... }
}
 
class E : D
{
  public override MoveAround(Point mousePosition) { ... }
}

Second, I think you should not use inheritance here. This way you get yourself locked pretty soon. Take for example what you have there now. You are implying that whatever control that can be selected (C) must also have an origin (C derives from B). I'm sure it makes sense to you now but as you develop you application further there may be some cases where this is not true and you end up hacking it. I think you should consider changing your design. Maybe interfaces will work better for you:
C#
interface IMoveable
{
    void MoveAround(Point mousePosition);
}

class D : A, IMoveable
{
    void MoveAround(Point mousePosition) { ... you logic goes here ... }
}

static void MoveObjects(Point mousePosition)
{
    foreach(IMoveable Obj in SomeKindOfList.OfType<IMoveable>())
    {
        Obj.MoveAround(mousePosition);
    }
}
 
Share this answer
 
I see what you are implying and I would generally agree with you but consider this please : As D only extends A I loose functionalities of C and B. So in fact I will always use class at its highest hierarchy class.

C#
public partial class MainWindow : Window
    {

        ObservableCollection<Object> Objects;
        ObservableCollection<ISelect> SelectedObjects;

        public MainWindow()
        {
            InitializeComponent();
            Objects = new ObservableCollection<Object>();
            SelectedObjects = new ObservableCollection<ISelect>();
            SelectedObjects.CollectionChanged += SelectedObjects_CollectionChanged;
        }

        private void SelectedObjects_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
                if (e.OldItems != null) foreach (ISelect DeselectObject in e.OldItems) DeselectObject.IsSelected = false;
                if (e.NewItems != null) foreach (ISelect SelectObject in e.NewItems) SelectObject.IsSelected = true;

        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            HalfCricleObjectMove Element = new HalfCricleObjectMove();
            MainCanvas.Children.Add(Element);

            Objects.Add(Element);

            Element = new HalfCricleObjectMove();
            MainCanvas.Children.Add(Element);

            Objects.Add(Element);
        }

        private void Window_MouseMove(object sender, MouseEventArgs e)
        {
            MoveObjects.MoveSelectedObjects(SelectedObjects, e.GetPosition(this));
        }

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            foreach (ISelect Object in Objects)
                if (Object.IsHit(e.GetPosition(this))) { SelectedObjects.Add(Object); break; }
        }

        private void Window_MouseUp(object sender, MouseButtonEventArgs e)
        {
            SelectedObjects.Clear();
        }


And now consider
MoveSelectedObject
s method :

C#
static class MoveObjects
    {
        internal static void MoveSelectedObjects(ObservableCollection<ISelect> SelectedObjects, Point mousePos)
        {
            foreach (ISelect Obj in SelectedObjects) (Obj as IMove).MoveObject(mousePos);
        }
    }


ISelect interface must also include IMove interface and this makes no sense in real world. I would hope to have interfaces in order like :

HalfCricleObjectSelect only implement ISelect interface
HalfCricleObjectMove only implement IMove interface

But in that case error occures in
MoveSelectedObjects
because
object that implements ISelect interface does not yet implement IMove interface.

Do you see problem?
 
Share this answer
 
v2
Comments
Tomas Takac 16-Nov-15 9:53am    
See this is why I said you should split it up. This is not a solution to your question, you should update your question with additional information, not post it as an solution.

I don't understand this, why one object cannot implement two interfaces?
HalfCricleObjectSelect only implement ISelect interface
HalfCricleObjectMove only implement IMove interface

If selecting and moving is essential in your system, and it is because it basically supports your infrastructure, then you should implement this functionality in the base class and not in subclasses or via interfaces.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900