Auf den Content eines TabItems zugreifen



  • Hi, ich erzeuge mit dem unten stehenden Code ein neues Tabitem in einem TabControl. Jetzt frage ich mich nur wie ich in einer anderen Funktion meiner Anwendung z.B. Linien, Rechtecke usw. auf dem Canvas plazieren kann bzw. wie ich auf das ausgewählte Tabitem und dessen Canvas zugreifen kann.

    private void AddCanvasTabItem(string Header)
            {
                TabItem tab = new TabItem();
                tab.Header = Header;
                tab.IsSelected = true;
                Page.Items.Add(tab);
    
                ScrollViewer scrollViewer = new ScrollViewer();
                scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
                scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
                tab.Content = scrollViewer;
    
                Canvas  canvas = new Canvas();
                canvas.Name = "Canvas";
                canvas.Height = 1000;
                canvas.Width = 1000;
                canvas.HorizontalAlignment = HorizontalAlignment.Left;
                canvas.VerticalAlignment = VerticalAlignment.Top;
                canvas.ClipToBounds = true;
                canvas.MouseWheel += new MouseWheelEventHandler(MouseWheelHandler);
                canvas.MouseLeftButtonDown += new MouseButtonEventHandler(MouseLeftButtonDownHandler);
                canvas.MouseMove += new MouseEventHandler(MouseMoveHandler);
                canvas.Background = new SolidColorBrush(Colors.White);
                scrollViewer.Content = canvas;
            }
    


  • Habe es selber hinbekommen. Falls es jemanden interessiert hier die Lösung:

    private void MouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e)
            {
                Rectangle r = new Rectangle();
                r.Fill = new SolidColorBrush(Colors.Blue);
                r.Stroke = new SolidColorBrush(Colors.Blue);
                r.Width = 100;
                r.Height = 100;
                r.SetValue(Canvas.LeftProperty, (double)240);
                r.SetValue(Canvas.TopProperty, (double)220);
    
                Canvas can = sender as Canvas;
                if (can != null)
                {
                    can.Children.Add(r);
                }
    }
    


  • Und noch eine Frage. Warum funktioniert folgender Codeausschnitt nicht?

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="de-DE.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    
        <Style x:Key="blabla">
        </Style>
    </Window.Resources>
    

    Die Fehlermeldung lautet: "Die Resources-Eigenschaft wurde mehr als einmal festgelegt."

    Ändere ich die Reihenfolge zu:

    <Window.Resources>
            <Style x:Key="blabla">
            </Style>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary
                        Source="de-DE.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Window.Resources>
    

    kommt die Fehlermeldung: "Alle zu "IDictionary" hinzugefügten Elemente müssen über ein Key-Attribut oder einen anderen ihnen zugeordneten Schlüsseltyp verfügen."

    Schreibe ich jetzt <ResourceDictionary x:Key="irgendwas"> dann kommt zwar keine Fehlermeldung mehr, aber das Dictionary wird nicht gelesen. Woran kann das liegen?



  • So habe die vermeintliche Lösung gefunden:
    http://stackoverflow.com/questions/1137725/c-wpf-add-window-style-even-if-a-resourcedictionary-already-exists
    Der Style wird einfach mit ins Resourcedictionary geschrieben. Leider hilft mir das in diesem Fall auch nicht weiter da x:Name im Bereich eines Resourcedictionary nicht zur Verfügung steht:
    http://msdn.microsoft.com/de-de/library/ms752290.aspx

    EDIT: Der x:Name des Styles war wohl doch überflüssig, habe ihn rausgenommen und jetzt geht es.



  • In die App.xaml packst Du das hier:

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="de-DE.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
    

    wobei Du MergedDictionaries nur benötigst, wenn Du hier mehr als eine Resource verwenden möchtest. Wenn nicht dann würde das hier ausreichen:

    <Application.Resources>
        <ResourceDictionary>       
            <ResourceDictionary Source="de-DE.xaml"/>
        </ResourceDictionary>
    </Application.Resources>
    

    und in die Windows.xaml dann deinen definierten Style

    <Windows.Resources>
        <Style x:Key="blabla" />
    </Windows.Resources>
    

    Aber dennoch, sollte dein Key gefunden werden, versuch diesen einfach mal mit DynamicResource einzubinden, dann bindet der diesen erst zur Laufzeit.

    also:

    <TextBlock Style="{DynamicResource blabla}" Text".." />
    

    Gruß Mario



  • Ok, das geht. Was ist jetzt aber, wenn ich folgenden Code habe:

    <Window.Resources>
    <StackPanel x:Key="LineWidth" Orientation="Horizontal">
        <TextBlock>Breite</TextBlock>
        <ComboBox x:Name="LineWidthComboBox"/>
    </StackPanel>
    ...
    

    Ich möchte im Codebehind das Stackpanel über den Key ( also (StackPanel)this.FindResource("LineWidth")) in eine Toolbar setzen. Leider ist so im Codebehind der Name der Combobox nicht verfügbar. Es wäre praktisch gewesen einfach per LineWidthComboBox.Items.Add(...); Elemente hinzufügen zu können. Man kann zwar auch über LogicalTreeHelper.FindLogicalNode(...,"LineWidthComboBox")auf die Combobox zugreifen, aber ich bin mir halt nicht sicher, ob es nicht doch eine elegantere Lösung gibt.



  • Hmm da scheint was nicht zu stimmen, in den Resources solltest du lediglich Styles definieren und nicht das Element selbst. Ich schreib Dir mal den kompletten Code zur Veranschaulichung:

    <Window x:Class="ForenHilfe.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    
        <Window.Resources>
            <Style x:Key="LineWidth" TargetType="StackPanel">
                <Setter Property="Orientation" Value="Horizontal" />
            </Style>
        </Window.Resources>
    
            <StackPanel Style="{DynamicResource LineWidth}">
                <TextBlock>Breite</TextBlock>
                <ComboBox x:Name="LineWidthComboBox" />
            </StackPanel>
    </Window>
    

    Nun kannst Du im CodeBehind auch auf das entsprechende Element zugreifen.

    LineWidthComboBox.Items.Add( ... );
    


  • Vielleicht sollte ich erstmal sagen was ich überhaupt gedenke umzusetzen. Und zwar soll je nachdem welches TabItem des HauptTabControl im Moment ausgewählt wurde z.B. der Content des ToolBars verändert werden. Beinhaltet das TabItem eine Textbox gäbe es z.B. eine Combobox mit der Schriftgröße oder wenn der Inhalt des TabItems ein Canvas ist soll eine Combobox mit der Linienbreite erscheinen. Allerdings sind die Elemente die ich in die Toolbar hinzufügen möchte manchmal auch komplexer, weshalb ich sie nicht im Codebehind erzeugen möchte.
    Meine Idee war jetzt dass ich unter den Window.Resources z.B. das schon genannte Object

    <StackPanel Style="{DynamicResource LineWidth}">
    <TextBlock>Breite</TextBlock>
    <ComboBox x:Name="LineWidthComboBox" />
    </StackPanel>
    

    unterbringe und dann wenn nötig in die Toolbar einbinde. Ich wollte also jedes mal wenn das TabItem gewechselt wird sozusagen die Toolbar wieder zerstören (und damit alle nicht gewünschten/passenden Elemente entfernen) und dann wieder mit den darzustellenden Elementen füllen. Bei deiner Lösung wäre das Element halt ständig im Toolbar eingebunden.
    Jetzt ist mir nur gerade die Idee gekommen, dass man ja auch von Anfang an alle Elemente in der Toolbar platzieren könnte und diese dann über das Visibility Attribut steuert. Wäre vielleicht sinnvoller. Dann könnte ich auch wieder über Name auf das Element zugreifen.

    EDIT:
    Jetzt habe ich aber neben dem eigentlichen HauptTabControl der Anwendung noch ein weiteres TabControl in dem ich beabsichtige unter anderem ein ProjektmappenTabItem zu platzieren. Der Code dafür sieht z.B. so aus:

    <TabItem x:Key="ProjektTabItem" Header="Projektmappe">
    <DockPanel>
        <ToolBar DockPanel.Dock="Top">
            <Button Width="22" ToolTip="Alle Dateien anzeigen">
                <Image Source="Resources/Images/Copy.png"/>
            </Button>
            <Button Width="22" ToolTip="Aktualisieren">
                <Image Source="Resources/Images/RefreshDocView.png"/>
            </Button>
        </ToolBar>
        <TreeView x:Name="treeview"/>
    </DockPanel>
    </TabItem>
    

    Jetzt kann man allerdings dieses TabItem auch schließen und soll es später auch wieder aufrufen können. Wie würde ich das denn jetzt realisieren, wenn ich den TabItem-Code nicht wie bisher in den Window.Resources speicher?



  • Student83 schrieb:

    Jetzt kann man allerdings dieses TabItem auch schließen und soll es später auch wieder aufrufen können. Wie würde ich das denn jetzt realisieren, wenn ich den TabItem-Code nicht wie bisher in den Window.Resources speicher?

    Spontan würden mir hier "CustomControls" einfallen welches von TabItem erbt, hier definierst Du genau dein TabControl so wie du es haben möchtest, und kannst es jederzeit wiederverwenden.

    Zu der Toolbar würde mir ein Singleton-Objekt einfallen, welches du via Interface durch alle Klassen "durchreichen" und manipulieren könntest.


Log in to reply