• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

softklin

[Resolvido] Clonar objectos gráficos

7 mensagens neste tópico

Boas pessoal. Estou a fazer umas experiências em C#, usando WPF. Mais concretamente, estou a fazer uma pequena aplicação de desenho de polígonos, e queria implementar um sistema de histórico, para poder anular alterações (undo e redo).

Estou a pensar fazer este sistema usando listas, nas quais cada nó tem um estado do canvas que estou a utilizar. O meu problema é que não sei como fazer clone dos objectos canvas (System.Window.Controls.Canvas), visto que não oferece um método clone, copy, wiseClone, ou similar. Estou a tentar copiar os objectos, visto que uma atribuição apenas aponta para o objecto original, correcto?

Se ajudar, a minha janela tem um objecto canvas, do tipo que já referi, e mais uns botões.

Desde já agradeço a vossa ajuda,

Cumps.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Existem alguns exemplos na net, mas quase todos com silverlight.

Talvez sirvam para o que queres, o que me parece mais adequado é o que se encontra no ultimo reply do post que te indico abaixo:

http://forums.silverlight.net/forums/t/1730.aspx

Parece relativamente simples, mas não tive a oportunidade de o testar.

Cumprimentos

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não estou a conseguir, ou pelo menos não estou a ter os resultados pretendidos. Optei por usar um código mais em cima, devido ao comentário do autor do último post:

I have put together a similar method using Linq just for fun.  Thismethod unfortunately does a horrible job of cloning some runtime types such as framework template, control template and so on.  I suppose i'llbe using the SilverlightContrib XamlWriter instead.

Não percebo bem se será o meu caso, no meu programa tenho um canvas que arrastei da toolbox.

Deixo aqui o código que estou a utilizar:

        /// <summary>
        /// Cria um ponto de histórico para o canvas actual
        /// </summary>
        void createRestorePoint()
        {
            Canvas backup = Clone(myCanvas);
            history.AddLast(backup);
        }

        /// <summary>
        /// Clona objectos
        /// </summary>
        /// <typeparam name="T">Tipo de dados genérico</typeparam>
        /// <param name="source">Dados a copiar</param>
        /// <returns>Cópia do objecto</returns>
        /// <see cref="http://forums.silverlight.net/forums/t/1730.aspx"/>
        private static T Clone<T>(T source)
        {
            T cloned = (T)Activator.CreateInstance(source.GetType());

            foreach (PropertyInfo curPropInfo in source.GetType().GetProperties())
            {
                if (curPropInfo.GetGetMethod() != null
                    && (curPropInfo.GetSetMethod() != null))
                {
                    // Handle Non-indexer properties
                    if (curPropInfo.Name != "Item")
                    {
                        // get property from source
                        object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] { });

                        // clone if needed
                        if (getValue != null && getValue is DependencyObject)
                            getValue = Clone((DependencyObject)getValue);

                        // set property on cloned
                        curPropInfo.GetSetMethod().Invoke(cloned, new object[] { getValue });
                    }
                        // handle indexer
                    else
                    {
                        // get count for indexer
                        int numberofItemInColleciton =
                            (int)
                            curPropInfo.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(source, new object[] { });

                        // run on indexer
                        for (int i = 0; i < numberofItemInColleciton; i++)
                        {
                            // get item through Indexer
                            object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] { i });

                            // clone if needed
                            if (getValue != null && getValue is DependencyObject)
                                getValue = Clone((DependencyObject)getValue);

                            // add item to collection
                            curPropInfo.ReflectedType.GetMethod("Add").Invoke(cloned, new object[] { getValue });
                        }
                    }
                }
            }

            return cloned;
        }


        private void setNewPoint(object sender, MouseButtonEventArgs e)
        {
            thePoints.Add(e.GetPosition(myCanvas));
            Polygon dot = new Polygon();
            dot.Points = createSquareFromCenter(e.GetPosition(myCanvas), 5);
            dot.Fill = new SolidColorBrush(Color.FromArgb(60, 0, 0, 0));
            myCanvas.Children.Add(dot);
        }


        private void button1_Click(object sender, RoutedEventArgs e)
        {
            //restaura para o primeiro ponto
            Canvas oldCanvas = Clone(history.First.Value);
            history.RemoveFirst();
            myCanvas = Clone(oldCanvas);
            myCanvas.UpdateLayout();
        }

Esta última função button1_Click é um botão de teste que tenho aqui para me restaurar para o primeiro ponto de todos. De seguida elimina-o. Não tem lógica, mas é só mesmo para testar o sistema.

Após a execução da função de restaurar, obtenho um canvas igual ao anterior, mas "congelado", porque a função setNewPoint, cujo objectivo é deixar algumas marcas de referências para os pontos dos polígonos, não está a funcionar, não cria pontos nenhuns... Nem uma simples função de apagar tudo funciona... O estranho é que não lança nenhuma excepção.

Quando vou à vista de debug, aparentemente está tudo bem, porque a lista de histórico fica com elementos do tipo Canvas.

Alguma ideia?  ;)

Notas:

- mycanvas é o objecto gráfico que arrastei da toolbox.

- Aquela função do meio, clone, foi a que copiei do website.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Vou testar aqui. Estive a ler o codigo todo, e pelo que percebi ele consegue clonar o objecto canvas. Não entendo no entanto porque o destroi de seguida.

history.RemoveFirst();

De certeza que alguma coisa me está a escapar mas ainda não consegui ver o quê. Talvez depois de um café se faça "luz" na mioleira, e consiga perceber.

Se conseguires depois mostra como conseguis-te. Agora fiquei curioso ;)

Cumprimentos

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

A intenção do history.removeFirst era para remover o item antigo com o qual ia substituir o canvas actual, myCanvas (por consequência vou tirá-lo do histórico).

A função que queria seria algo mais deste género:

//restaura para o ponto anterior
Canvas oldCanvas = Clone(history.Last.Value);
history.RemoveLast();
myCanvas = oldCanvas; //Clone(oldCanvas);
myCanvas.UpdateLayout();

Os canvas vão sendo guardadsos por ordem cronológica conforme vão sendo alterados e depois quando é feito um pedido para anular alterações, vou buscar o último estado gravado, e elimino-o do histórico.

Mas bem, quanto ao problema do clone, mantém-se... Vou pesquisar mais um pouco a ver se encontro alguma coisa, ou outra solução.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não sei se ajudará, mas também andei a pesquisar exemplos, a ver se percebia o que se pode estar a passar.

No codeproject encontrei este, mas um  graphis engine. Pelo que vi tem uma abordagem diferente da que penso que estejas a fazer ao mesmo problema. Talvez ajude um pouco. Pelo que percebi é mais complicado trabalhar apenas com canvas.

http://www.codeproject.com/KB/dotnet/another_graphic_engine.aspx

"Q: Why is the GraphicDocument a separate class from Canvas? I could add all the items to draw directly to the Canvas.

A: Same reason of the first question: the GraphicDocument is the logical items aggregation, it is the document, the canvas is the surface on which the document is drawn.

And again, in this way, I could have the same document open on more canvas."

Já percebi a questão do history. Foi comfusão minha literalmente.

Cumprimentos

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Apocsantos, obrigado por toda a ajuda. Graças a ti, consegui chegar à solução dos clones, pelo menos como eu que queria.

Resolvi alterar o meu projecto, e considerar um canvas criado programaticamente, me vez de arrastado da toolbox. Também mudei a minha função de clone para uma baseada em parsers de XML, que encontrei em:

http://stackoverflow.com/questions/32541/how-can-you-clone-a-wpf-object

// gravar o canvas actual (myCanvas) para XAML
String curCanvasCode = XamlWriter.Save(myCanvas);
StringReader stringReader = new StringReader(curCanvasCode);
XmlReader xmlReader = XmlReader.Create(stringReader);
// ler o canvas do código XAML e passá-lo para objecto
Canvas backup = (Canvas)XamlReader.Load(xmlReader);

E funciona, com um pequeno senão: animações, handlers (funções associadas a cliques, etc) são perdidas na transição para XAML, porque esse código é escrito à parte em ficheiros C#/VB. Nesse caso basta, ao recuperar a cópia, definir de novo estes atributos. A título de exemplo, a minha função ficou assim:

// buscar o canvas anterior ao histórico e obter o código XAML
String prevCanvasCode = XamlWriter.Save(history.Last.Value);
StringReader stringReader = new StringReader(prevCanvasCode);
XmlReader xmlReader = XmlReader.Create(stringReader);
Canvas prevCanvas = (Canvas)XamlReader.Load(xmlReader);

// remover o canvas actual dos controlos
myGrid.Children.Remove(myCanvas);
// o canvas actual é agora o anterior (referência)
myCanvas = prevCanvas;

// redefinir aributos, handlers, etc, perdidos na transição para XAML
prevCanvas.MouseLeftButtonDown += new MouseButtonEventHandler(setNewPoint);

// colocar o canvas anterior (agora actual) nos controlos da janela
myGrid.Children.Add(prevCanvas);

Funciona bem no meu caso. Mais uma vez, obrigado pelos links e pela atenção. ;)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora