Entwickler-Ecke

WPF / Silverlight - Canvas/CustomItemsControl Adorner der Children clippen nicht


IsNull - Sa 05.02.11 20:48
Titel: Canvas/CustomItemsControl Adorner der Children clippen nicht
Hallo,

Ich nutze folgendes Control um Map-Tiles anzuzeigen: http://greatmaps.codeplex.com. Meine Applikation folgt dem MVMV Pattern.
GMap.NET's GMapControl nutzt intern ein Canvas, um z.B. Map Marker darzustellen.

Mein Problem ist nun folgendes:
Schiebt man einen MapMarker (ist ein beliebiges UserControl) ausserhalb des sichtbaren Bereiches wird das wunderbar geclipt. Nun habe ich für die Marker einen Adorner definiert, um deren Selektionsstatus zu visualisieren - und diese Adorner werden nun quer über die gesammte Applikation gerendert. Sieht recht ulkig aus:

http://img812.imageshack.us/img812/9277/adornerissue.png

Ich hab mich zum Thema Adorner belesen, und die vermeintliche Lösung zum Problem ist einfach: Vom Marker wird der nächst höhere (Parent) AdornerLayer gesucht - und wenn ich meine Map mit einem <AdornerDecorator></AdornerDecorator> umschliesse sollte sich der Adorner nicht mehr auserhalb von diesem rendern lassen.
Das funktioniert nur leider nicht.


XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
    <!-- DeliveryMapView -->
    <!-- Expects a set DataContext to a DeliveryMapViewModel Instance-->
    <Grid>
        <AdornerDecorator>
                <ContentControl ClipToBounds="True" Content="{Binding MapControl}">
                    <ContentControl.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="Add to Route" Command="{Binding AddSelectionToActiveRouteCommand}" />
                            <MenuItem Header="Remove from Route" Command="{Binding RemoveSelectionFromActiveRouteCommand}" />
                        </ContextMenu>
                    </ContentControl.ContextMenu>
                </ContentControl>
        </AdornerDecorator>
    </Grid>


Irgendjemand ne Idee was das sein könnte?

Abweichungen vom Standard:
Es gibt zwei Dinge, die von den 08/15 Beispielen abweichen:

Die Adorner definiere ich für gewöhnlich in xaml, mit diesen Klassen: http://www.codeproject.com/KB/WPF/adornedcontrol.aspx
Grundsätzlich wird so das Control von einem AdornedControl : ContentControl umschlossen, und im Code davon passiert das unspektakuläre: this.adornerLayer = AdornerLayer.GetAdornerLayer(this);

GMap.Net ist leider nicht ganz MVVM freundlich, daher hat mein DeliveryMapViewModel ein Property MapControl was einer Instanz des GMap.NET GMapControls entspricht, und dieses binde ich dann wie oben zu sehen ist an den ContentControl.Content.

Grüsse


Kha - Sa 05.02.11 21:32

Ich habe noch nicht ganz den Überblick gewonnen, aber ob etwas auf dem richtigen AdornerLayer landet, solltest du leicht mit Suche bei Google SNOOP herausfinden können.


IsNull - So 06.02.11 10:46

Hallo Kha,

Danke für den Tipp mit Snoop - das hat folgendes zu Tage gefördert:
http://img132.imageshack.us/img132/2963/snoopadornerissue.png

Wenn ich den Roten Rahmen richtig interpretiere, sollte der die Grenzen des AdornerLayers darstellen - genau dort sollte die Grenze auch sein. Aber die Adorners scheinen sich nicht darum zu kümmern, dass sie ausserhalb von diesem sind.

Lass mich wissen, was dir unklar ist an meinen Erläuterungen, ich versuche es dann besser zu umschreiben.


Kha - So 06.02.11 14:41

Hm, da weiß ich ehrlich gesagt auch nicht weiter :nixweiss: . Nach einer kurzen Google-Suche scheint das Problem der meisten Leute zu sein, dass sich das Clipping von AdornerLayers gar nicht abschalten lässt :gruebel: .


IsNull - Mo 07.02.11 09:45

Ist mir auch aufgefallen dass die meisten über den AdornerLayer hinaus (teilweise sogar über das Fenster hinaus) ihre Adonrner Rendern wollen.

Nach einigen Tests hab ich es AFAIK raus. Ich habe das ganze in ein Testprojekt ausgelagert.

Aus dem hier (wo das clipping nicht funktioniert)

XML-Daten
1:
2:
3:
        <Canvas ClipToBounds="True">
                    <!-- Items with Adorners -->
        </Canvas>


Habe ich das hier gemacht:

XML-Daten
1:
2:
3:
4:
5:
6:
7:
        <Canvas ClipToBounds="True">
            <AdornerDecorator>
                <Canvas>
                    <!-- Items with Adorners -->
                </Canvas>
            </AdornerDecorator>
        </Canvas>

So clippen die Elemente korrekt.



Ich stehe nun leider noch vor der Herausforderung, diese verschachtelten Elemente in Code in das GMapsControl hineinzupatchen. Original sieht das ca so aus:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
class GMapControl : ItemsControl, Interface
{
        public GMapControl() {
                var factoryPanel = new FrameworkElementFactory(typeof(Canvas));
                factoryPanel.SetValue(Canvas.IsItemsHostProperty, true);

                ItemsPanelTemplate template = new ItemsPanelTemplate();
                template.VisualTree = factoryPanel;
                ItemsPanel = template;


                Style st = new Style();
                {
                    st.Setters.Add(new Setter(Canvas.LeftProperty, new Binding("LocalPositionX")));
                    st.Setters.Add(new Setter(Canvas.TopProperty, new Binding("LocalPositionY")));
                    st.Setters.Add(new Setter(Canvas.ZIndexProperty, new Binding("ZIndex")));
                }
                ItemContainerStyle = st;

            // [...]
        }
// [...]
}


Meine Versuche hier ein AdornerDecorator sowie ein Child einzufügen sind mangels meiner Erfahrung was Templates im Codebehind angeht gescheitert. Hast du evtl einen Tipp wie ich hier den AdornerDecorator + Canvas noch reinschachteln kann?

Edit: Problem gelöst, Lösung folgt sogleich :)


IsNull - Mo 07.02.11 10:32

Die Lösung des Problems ist ganz simpel. Hat man ein Items Control (egal was), und will dass dessen Adorner nicht darüberhinaus gerendert werden, muss man es wie bei msdn zu lesen erstmal mit einem AdornerDecorator umschliessen.


XML-Daten
1:
2:
3:
4:
5:
            <AdornerDecorator>
                <Canvas>
                    <!-- Items with Adorners -->
                </Canvas>
            </AdornerDecorator>


Leider clippen die Element so immer noch nicht - ClipToBounds=True löst das:

XML-Daten
1:
2:
3:
4:
5:
6:
 
            <AdornerDecorator ClipToBounds="True">
                <Canvas>
                    <!-- Items with Adorners -->
                </Canvas>
            </AdornerDecorator>


Edit: Vereinfacht.

Edit: Jetzt nerve ich mich gerade. Eigentlich offensichtlich, dass ClipToBounds nötig ist - obwohl in der msdn nie ein Wort darüber verloren wird. Naja ;)