Laden...
L
Benutzerbeschreibung

Forenbeiträge von Lector Ingesamt 862 Beiträge

26.10.2009 - 18:46 Uhr

Ich arbeite derzeit an einem sehr großen Projekt und benutze auch MVVM. Die Trennung von View und Logik ist sinnvoll aber ich muss talla in vielen der oben genannten Punkten rechtgeben.
Z.B. ist der Ansatz die CodeBehinds leer zu lassen an sich schön. Weniger schön finde ich dagegen dies zwanghaft durch 'erzwungene' Commands, RoutedEvents w/e zu erzwingen.
In 90% meinen Views besteht die Code-Behind-Datei auch nur aus einem InitializeComponent-Aufruf aber es gibt immer noch Fälle in denen man doch den einen oder anderen kleinen Aufruf in 5 Zeilen Code macht bevor man sich einige WPF-Wrapper-Klassen schreibt die dann irgendein Event in einen Command umzubiegen. Diese Codezeilen können durchauch so gestaltet sein dass sie nicht bei jeder GUI-Änderung angepasst werden.
Um ein einfaches Beispiel zu nennen: Wir haben eine ListBox und weisen den ItemsSource im Code zu. (Ich benutze z.B. CLINQ und solche Querys lassen sich nicht in XAML abbilden).
Wenn ich jetzt in XAML die ListBox durch eine ComboBox ersetze funktionert es trotzdem noch solange ich den Namen des ItemsControls nicht ändere. Ich finde nicht dass der Code durch solch kleine Code-Schnipsel unübersichtlich wird. Vorraussetzung ist natürlich dass ich bei Änderungen die Code-Behind-Files nicht ignoriere.

Was die Ableitung von Window angeht so muss ich MrEvil zustimmen. Das kann in der Tat sinnvoll sein. Ich habe in meiner Controls-Library auch ein ExtendedWindow in dem ich z.B. Commands wie Minimize, Maximize oder Close implementiere. Ich habe auch von div. anderen Controls abgeleitet und sie um Funktionalitäten erweitert welche durch AttachedProperties nicht möglich sind.

Was die anderen Pattern angeht so kann ich leider nicht viel dazu sagen da ich sie noch nicht (bewusst) verwendet habe. Grundsätzlich bin ich aber auch nicht so verklemmt um zu behaupten dass MVVM das einzige gute WPF-Design-Pattern ist.
MVVM hat schließlich auch Nachteile welche von den MVVM-Befürwortern, wie talla wohl auch gemerkt hat, einfach verschwiegen wird. Grundsätzlich halte ich es allerdings für ein gutes Design-Pattern solange man sich nicht künstlich auf XAML-Only einschränkt. Gewisse Dinge erforden nun mal die ein oder andere Zeile Code. Das mag nicht perfekt sein aber immer noch besser als zig Workaround-Klassen zu machen nur damit auch ja nichts im Code-Behind steht.

22.10.2009 - 11:52 Uhr

So ich bin durch Google jetzt doch auf die Ursache des Problems gestoßen:

http://social.msdn.microsoft.com/forums/en-US/wpf/thread/fa2053f3-3272-425a-bb81-95e3252acb7c/

Hintergrund ist wie offiziell bestätigt ein Bug in WPF. Da der Betrag schon 2 Jahre alt ist vermute ich dass es nicht mehr behoben wird.

Wer das gleiche Problem hat kann es durch folgenden Codeschnipsel im Hauptfenster lösen:


protected override void OnKeyUp(KeyEventArgs e)
    {
      base.OnKeyUp(e);
      if ((e.Key == Key.LeftAlt) || (e.Key == Key.RightAlt) || (e.Key == Key.System))
      {
        // Do nothing...
        e.Handled = true;
        return;
      }
    }

22.10.2009 - 11:38 Uhr

Rechtsklick auf Solution -> Hinzufügen -> Neues/Vorhandenes Projekt
Das sollte funktionieren

22.10.2009 - 11:17 Uhr

Man kann mit Sicherheit CustomControls machen. Das funktioniert aus Assembly-übergreifend. Ich habe in meinen Projekt auch eine Controls-Lib.

Ich glaube du hast mein Einbinden des Projekts irgendwas falsch gemacht. Was jetzt genau nicht stimmt kann ich dir leider auch nicht sagen.

22.10.2009 - 10:48 Uhr

Wenn dein Control eigendlich kein Control sein soll gibt es sicherlich andere Möglichkeiten:

Die einfachste Möglichkeit wäre den DataContext im Code zu setzen. Wenn auch nicht die sauberste. Aber immer noch sauberer als ein erzwungenes Control.

Die sauberste wäre es den DataContext einfach per Binding zuzuweisen. Wenn das nicht so einfach geht kannst du ja Converter oder AttachedProperties verwenden.

Vielleicht hilft dir das ja weiter.

22.10.2009 - 10:42 Uhr

Hallo,

Ich habe folgendes Programm:


<Window x:Class="CursorFocusTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas>
        <UserControl Width="100" Height="100" Background="Blue" Cursor="None"/>
    </Canvas>
</Window>

Der Mauszeiger soll im UserControl ausgeblendet werden. Später soll sich innerhalb des UserControls ein weiteres UserControl befinden welches den Mauszeiger ersetzt.

Wenn ich die Alt-Taste drücke währen ich mit der Maus im UserControl bin sehe ich im ganzen Fenster keinen Mauszeiger mehr bis ich mit der Maus klicke oder eine beliebige Taste drücke.
Umgekehrt genauso: Drücke ich Alt während ich mit der Maus über den blanken Fenster bin sehe ich im UserControl den Windows-Mauszeiger bis ich Klicke oder eine Taste drücke.

Wie kann ich das verhindern???

22.10.2009 - 09:25 Uhr

Daran kann es glaube ich nicht liegen weil das UserControl OHNE Unterelement das Event auch nicht bekommt.
Füge ich einen Button hinzu bekomme ich das Event. Es wirkt eher so als ob ich bestimmte UserControl brauche um das Event zu bekommen.

Aber ich habe mittlerweise herausgefunden dass ich das Event auch ohne Unterelement bekommen kann wenn ich das UserControl als Focusable=true markiere und den Fokus darauf setze.

Eine Lösung scheint das allerdings auch nicht zu sein da ich die Keys auch abfangen möchte wenn andere Steuerelemente den Fokus haben.

Hat jemand Lösungsvorschläge?

21.10.2009 - 17:54 Uhr

Solange der Speicher wieder freigegeben wir sehe ich da auch kein Problem. Es sind aber auch schon Fälle bekannt bei denen WPF den Speicher nicht mehr freigibt.
Z.B.:*3D mit VisualBrushes auf Rechnern ohne Hardwareunterstützung *Ein Transformed-Bitmap-Objekt auf einem ImageControl

Wenn der Speicher erst wieder freigegeben wird wenn die Anwendung beendet wird wäre das ein Problem. In deinem Fall würde ich mir keine Sorgen machen.

Zur Info: Mit GC.Collect(); Kannst du den Speicher manuell aufräumen (lassen).

An deiner Stelle würde ich das allerdings C# überlassen.

21.10.2009 - 17:39 Uhr

Hallo,

Ich habe ein UserControl erstellt und mich dort aufs KeyDown (bzw. PreviewKeyDown) registriert. Dieses Event kommt beim Drücken einer Taste jedoch nicht an.

Ich habe mal mit dem Innhalt des UserControls herumexperimentiert. Ist ein Button oder InkCanvas enthalten bekomme ich auf einmal das Event. Ist das UserControl ohne Inhalt bzw. ist nur ein Label, TextBlock enthalten wird das KeyDown ignoriert. Mehr Controls habe ich nicht ausprobiert.

Meine Frage ist nun: Warum verhält sich das so?
Und wie kann ich das KeyDown-Event bekommen ohne einen Button zum UserControl hinzuzufügen???

15.10.2009 - 09:25 Uhr

Ich denke auch dass du 3D nehmen musst. So eine Trapetztransformation ist unter 2D nur mit Shadereffekten möglich.

12.10.2009 - 13:19 Uhr

Fall für Converter und ConverterParameter würde ich sagen.

12.10.2009 - 11:05 Uhr

Es gibt einen ConverterParameter den du im Binding-Converter beachten kannst sofern du einen Converter benutzt.
Ansonsten kannst du noch StringFormat benutzen um einen Text anders zu formatieren.

12.10.2009 - 10:09 Uhr

Hallo,

Ich habe in meinem Programm eine Druckfunktion welche ein Bild drucken soll. Dazu erstelle ich ein XPS-Dokument und zeige es per DocumentViewer an. Dazu gibt es noch einen Print-Button welcher per ApplicationCommands.Print den DocumentViewer dazu bringt einen Default-Druck-Dialog anzuzeigen. Das funktioniert soweit.

Da die verschiedenen Bilder unterschiedliche Seitenformate haben erstelle ich die XPS-Seite bei Bedarf im Querformat damit die maximale Fläche einer Seite ausgenutzt wird (Pro Seite immer 1 Bild). Auch das funktioniert wunderbar und wird in der Druckvorschau (DocumentViewer) so angezeigt. Wenn ich das Querformat jedoch drucken will kommt trotzdem ein Hochformat-Bild raus bei dem die Hälfte abgeschnitten ist. Das Problem würde sich lösen lassen indem man im Druckdialog einstellt ob Hoch- oder Querformat gedruckt werden soll. Diese Arbeit möchte ich jedoch dem Benutzer ersparen und programmtechnisch lösen.

Hat irgendjemand Lösungsvorschläge?

08.10.2009 - 16:04 Uhr

In diesem Fall wäre es vielleicht sinnvoll das propdp im UserControl zu erstellen. Dann kannst du die Expander dran hängen. Vom Window aus kannst du ja auf das UserControl zugreifen und dort das Propdp aktualisieren.

Wie du das Binding machst steht hier: [GELÖST] Auf eigene Eigenschaften im eigenen Fenster über Xaml zugreifen
Du hast nur ein UserControl statt einem Fenster. Das macht aber keinen Unterschied

07.10.2009 - 11:46 Uhr

Also das mit dem foreach stellt kein Problem da denn die toRemove-Collection im Beispiel oben ist eine komplett andere Collection ans die myObsCol.

BindingList<T> kannte ich noch nicht und scheint zu funktionieren. Was mich allerdings stört ist dass die gesamte Liste resetet werden muss.

Ein weiterer Ansatz den ich ausprobiert habe war eine eigene ObsCol welche Methoden wie Add-/Remove-/InsertRange implementiert und dabei nur EINMAL CollectionChanged feuert. Diese hat ihren Dienst auch wunderbar getan sobald jedoch eine ListBox daran gebunden ist bekomme ich beim feuern meines eigenen Changed-Events eine NotSupportedException mit den Hinweis dass Bereichsaktionen nicht von der ListBox unterstützt werden 😦.
Anscheinend bleibt mir nichts anderes übrig als entweder die komplette Liste neu aufzubauen oder mehrere Remove-Aktionen hintereinander auszuführen...

07.10.2009 - 10:42 Uhr

In diesem Fall machst du in dem UserControl welches den ToggleButton beinhaltet ein DependencyProperty. Dieses aktualisierst du sobald der User den ToggleButton ein-/ausmacht.
Die Expander bindest du dann nicht mehr direkt an den Toggle-Button sondern an dieses Hilfs-DependencyProperty des UserControls auf dass du ja zugreifen kannst.

07.10.2009 - 10:03 Uhr

Hallo,

Ich habe eine ObservableCollection in der ich Objekte Speichere.
Dann habe ich noch eine ListBox welche di ObsCol anzeigt. Wenn ich die ObsCol verändere verändert sich auch die View.

Nun mein Problem:

Ich möchte gerne MEHRERE Elemente aus der ObsCol entfernen. Zwischen den Vorgängen soll KEIN Update erfolgen. Leider hat die ObsCol nur eine Remove und keine RemoveRange-Methode... Ich muss also soetwas machen:


foreach(MyViewModel vm in toRemove)
{
     myObsCol.Remove(vm);
}

Angenommen wir würden 5 Elemente entfernen würde sich die ListBox 5 mal erneut aufbauen.

Ein weiteres Problem dass auftritt: Meine ListBox ist nicht direkt an die ObsCol gebunden sondern an eine komplexe CLINQ-Query mit Select().Where().OrderBy()-Klausel.
Wenn dieser zwischen den Remove-Zuständen ausgeführt wird kommt eine Exception da sich die ObsCol in einem inkonsistenten Zustand befindet.

Ein weiteres Problem. Meine ObsCol ist in einer allgemeinen Klasse definiert und weis nichts von der ListBox. Ein temporäres abhängen der ListBox ist also nicht möglich.

Weis irgendjemand wie ich einen RemoveRange mit nur einer UpdateNotification machen kann bzw. kennt jemand eine andere Lösung für dieses Problem???

07.10.2009 - 09:54 Uhr

Versuch doch mal sowas:


<ToggleButton Name="toggleButton" Content="Aus/Einklappen"/>
...
<Expander IsExpanded="{Binding ElementName=toggleButton,Path=IsChecked}"/>

06.10.2009 - 12:03 Uhr

Hier mein Lösungsansatz:

Setzte doch die Opacity-Eigenschaft eines Elements auf 0.5 nachdem du die Animation gestartet hast.
Dann registrierst du dich auf das Completed-Event der DoubleAnimation. Wenn die Animation fertig ist kannst du die Opacity wieder zurücksetzen.

Im Completed-Event musst du natürlich noch das entsprechende Child zuordnen können. Ausserdem musst du auch daran denken dass ArrangeOverride evtl. noch während einer alten Animation aufgerufen wird.

05.10.2009 - 13:19 Uhr

Möglichkeit 1:
Per VisualTreeHelper alle Expander suchen und expanden (davon rate ich jedoch ab)

Möglichkeit 2:
Mache ein DependencyProperty und binde die IsExpanded-Eigenschaft sämtlicher Expander an dieses DependencyProperty.
Evtl kannst du auch ein bestehendes DependencyProperty benutzen. Z.b. die IsChecked-Eigenschaft eines ToggleButtons.

01.10.2009 - 11:57 Uhr

Hallo,

Ich bin gerade dabei ein 3DPanel zu implementieren welches Elemente auf V2DV3D-Elementen hostet. Mein Panel erbt von VirtualizingPanel. Ich habe einen Viewport3D welchen in in Measure/Arrange als einziges Element zu den Children hinzufüge.

Im Measure erstelle ich für jedes Child ein V2DV3D-Element und im Arrange werden noch die Transformationen dieser Elemente angepasst.

Mein Problem: Es wird nichts angezeigt. Sobald ich jedoch V2DV3D durch ModelVisual3D ersetze werden die Elemente gerendert (nur nicht interaktiv). Ich versuche nun schon seit Stunden das Problem zu lösen komme aber einfach nicht drauf was das Problem sein könnte. An der 3D-Szene liegt es garantiert nicht da einfach ModelVisual3D-Elemente richtig platziert werden. Wenn ich Dummy-Buttons als Visuals benutze wird ebenfalls nicht gerendert.

Ich bin für Lösungsvorschläge dankbar.

23.09.2009 - 16:43 Uhr

Hallo,

Ich hoffe ich bin im richtigen Forum. Ich habe eine Frage zum PrintDialog in WPF:

Ich möchte in meiner Anwendung ein Dokument drucken. Dieses XPS-Dokument erstelle ich selbst. Die Seite im Dokument mache ich so groß wie die PrintableArea welche der PrintDialog liefert. Beim drucken des Dokuements (immer 1 Seite) wird auch genau eine Seite ausgegeben. Da der Drucker den Rand allerdings nicht bedrucken kann werden Teile des Dokuments abgeschnitten. Nun wäre es für mich kein Problem das Dokuement einfach kleiner zu machen würde aber gerne wissen wie groß der nicht bedruckbare Bereich ist. Wie kann ich diesen auslesen bzw. wie komme ich überhaupt an den vom User ausgewählten Drucker???

16.09.2009 - 09:55 Uhr

Dafür gibt es den TemplateSelector.

14.09.2009 - 16:01 Uhr

Gibt es irgendeine andere Möglichkeit Elemente wie oben beschrieben zu platzieren?
Das muss doch gehen ohne dass ich gleich ein eigenes Panel schreiben muss...

Das Panel soll übrigens als ItemsPanel für ein ItemsControl dienen. Die Elemente manuell zu manipulieren wird also leider nicht funktionieren.

14.09.2009 - 15:29 Uhr

Hallo,

ein UniformGrid ordnet die Elemente normalerweise von links nach rechts an:

<UniformGrid Rows="3"/>

1 2  3  4
5 6  7  8
9 10 11 12

Wie kann ich bewirken dass das Panel von oben nach unten gefüllt wird?


1 4 7 10
2 5 8 11
3 6 9 12

11.09.2009 - 17:51 Uhr

Schau dir mal die Data-Template-Eigenschaft der TreeView an.

11.09.2009 - 17:46 Uhr

Hallo,

Ich bekomme seit einigen Tagen vom VS eine Kompilierwarnung "Das Ereignis ... wird nie verwendet" was unschön ist, ich jedoch nicht anders zu lösen weis.
Es hat folgenden Hintergrund:

Ich habe eine Klasse welche das Interface ICommand aus der WPF-Bibliothek implementiert. Dieses Interface besteht aus 2 Methoden welche ich implementiere und einen Event.
Das Event sollte man feuern sobald sich die Ausführbarkeit des Commands ändert was bei dieser Klasse jedoch nie der Fall ist. Folglich meldet der Compiler dass ein nicht verwendetes Event deklariert ist, welches ich jedoch wegen dem Interface auch nicht weglassen kann.
In der MSDN habe ich eine Möglichkeit gefunden wie ich einzelne 'Code-Analyzer-Warnungen' per SupressMessage-Attribut unterdrücken kann: Übersicht über die Unterdrückung im Quellcode
Das hilft mir in meinen Fall allerdings nicht weiter da es sich um eie Kompiler-Warnung handelt. Diese ließen sich nur global für das komplette Projekt ausschalten was ich jedoch nicht für angemessen halte: /nowarn (Angegebene Warnungen unterdrücken) (C#-Compileroptionen)

Nun würde ich gerne von euch wissen ob ihr das Problem auch schon hattet und wie ihr damit umgegangen seid? Mit meiner momentanen Lösung 'Problem ignorieren' bin ich nicht zufrieden weis aber auch keine saubere Lösung.

11.09.2009 - 11:33 Uhr

Vielleicht solltest du schon beim Auswählen einer Ellipse eine entsprechende Eigenschaft setzten.

Ein anderer Ansatz wäre jetzt das verwenden einer ListBox. Layout und so kannst du ja entsprechend hinbiegen.

11.09.2009 - 09:13 Uhr

Versuch mal VisualTreeHelper.GetParent(ellipse).

08.09.2009 - 10:23 Uhr

Hallo,

Ich bin dabei eine Methode zu implementieren welche einen Verzeichnissnamen validieren soll.
WICHTIG: Es handelt sich nicht um den Namen eines kompletten Pfades sondern lediglich um einen einzelnen Ordner!

Die Methode sieht momentan so aus:


public static Boolean isValidDirName(String strName)
    {
      if (String.IsNullOrEmpty(strName))
        return false;

      foreach(Char cInvalidChar in Path.GetInvalidFileNameChars().Union(Path.GetInvalidPathChars()))
      {
        if (strName.Contains(cInvalidChar.ToString())) return false;
      }

      //Ansonsten gilt der String als valide
      return true;
    }

Momentan werden nur ungültige Zeichen geprüft. Ich habe jedoch durch Zufall herausgefunden dass Punkte und Leerzeichen am Ende eines Ordners grundsätzlich zugelassen, beim Erstellen/Umbenennen eines Ordners jedoch weggelöscht werden.
Punkte am Anfang eines Ordners sind zulässig und bleiben bestehen, was jedoch vom Windows-Explorer verboten wird.

Meine Frage ist nun: Wie finde ich heraus ob ein Verzeichnissname in seiner ursprünglicher Form übernommen werden kann, oder vom Filesystem/Windows verändert wird???

04.09.2009 - 10:36 Uhr

Du könntest doch die TrackBall Klasse um ein Attached-Property erweitern welche es ermöglicht bestimmte Elemente von der Transformation auszuschließen.


<DirectionalLight
                tools:TrackBall.IgnoreTransform="True"
                Color="White"
                Direction="-2,-3,-1" />

So würde das dann aussehen wenns fertig ist. Geht natürlich nur wenn 3D-Tools OpenSource ist.

03.09.2009 - 09:03 Uhr

Ja die Änderungen des Objekts passieren im Nicht-GUI-Thread.
Wenn das Objekt welches geändert wird jetzt ein Dispatcher-Object wäre würde das zu einer Exception führen. Da es sich jedoch nur um ein 'normales' Objekt handelt kannst du Änderungen vornehmen.

02.09.2009 - 18:40 Uhr

Hallo,

Ich habe ein kleines Problem mit einem ErrorTemplate. Wenn die Validierung in meinen TextBoxen fehlschlägt möchte ich einen FehlerText über der TextBox platzieren welche sich jedoch ausblenden soll wenn die TextBox den Focus hat.

Das Anzeigen des Textes funktioniert wunderbar. Nur scheint der Trigger, welcher für das Ausblenden zuständig ist keinerlei Wirkung zu zeigen.


<Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Control}">
                    <Grid>
                        <AdornedElementPlaceholder x:Name="placeHolder"/>
                        <TextBlock Foreground="{DynamicResource ErrorBrush}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="4,4,4,0"
                                   Text="{Binding ElementName=placeHolder,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" IsHitTestVisible="False" Name="info"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsFocused" Value="True">
                            <Setter TargetName="info" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

Weis jemand wo der Fehler liegt?

02.09.2009 - 12:13 Uhr

Es funktioniert weil den Objekt kein DispatcherObject mehr ist.
Wenn du im 2. Thread etwas an der ObservableCollection ändern würdest würde es nicht klappen. Ein reiner Lese-Zugriff auf ein Element scheint jedoch zu funktionieren.

01.09.2009 - 16:37 Uhr

Versuche mal das Erstellen der Objekte im GUI-Thread auszuführen. Wenn Agents DependencyObjects sind sind sie auch DispatcherObjects. Das könnte zu Problemen führen wenn du im Nicht-GUI-Thread darauf zugreifst. Allso am besten immer Invoke machen wenn du was mit diesen Objekten machst.

28.08.2009 - 12:15 Uhr

Mich würde ja mal interressieren was MS dazu sagt.

Hast du schon mal versucht das wörtchen 'abstract' für das Design einfach mal kurz auszukommentieren?

28.08.2009 - 11:05 Uhr

Kann ich leider nicht testen da ich nur das Blend 2 habe und die Templates immer in XAML direkt verändere.
Versuch mal von ButtonBase abzuleiten und teste ob du da das Template verändern kannst.

28.08.2009 - 09:33 Uhr

Controls als abstrakt zu definieren ist mit Sicherheit zulässig. Microsoft hat das mit ButtonBase ja genauso gemacht.

Wenn für dein BasisControl ein Default-Style mit Template implementiert ist sollten die abgeleiteten Controls diesen auch verwenden.

Wenn ich von Button ein Control ableite sieht dieses auch einfach nur wie ein Button aus.
Wenn ich allerdings einen eigenen Style für Button definiere um den Default-Button zu verändern und diesen dann ohne Key in ein globales ResourceDict lade wird er nur auf Buttons und nicht auf davon abgeleiteten Klassen angewendet.

Für das von dir beschriebene Szenario sehe ich aber erst mal keine großen Hindernisse.

27.08.2009 - 11:36 Uhr

Minimalbeispiel:


<ListBox x:Name="listBox"/>


for(int i=0; i<10; i++) listBox.Items.Add(1);

Bei diesen Beispiel kann man alle Items der ListBox gleichzeitig auswählen.

Gemerkt habe ich diese Annomalie bei einer Datums-Liste in meiner Anwendung. Dort gibt es noch ein Editierfeld in dem man das selektierte Datum ändern kann. Wenn man dort das unterste Datum auswählt und ein bestehendes Datum auswählt wird auf einmal ein anderes Datum selektiert. Wenn man jetzt das Datum Tageweise per Repeat-Button verstellt kann es dann auch leicht passieren dass man das vorher nicht ausgewählte Datum ändert. Bleiben 2 gleiche DateTimes stehen markiert man immer beide.

Hat lange gedauert bis ich gemerkt habe wo eigendlich der Fehler lag und habe wie oben bereits genannt einen Referenztypen um DateTime gestrickt.

Mir ist auch aufgefallen dass diese ungewollten Index-Änderungen nur in eine Richtung funktionieren. D.h. die Listbox spring von Index 6 zu Index 2 nie aber zu einem höheren Index.

27.08.2009 - 09:00 Uhr

Hat jemand schon mal ein Element mehrmals in eine ListBox hinzugefügt? Danach funktioniert die Selektierung nicht mehr richtig.

Wenn man z.B. Int-Zahlen hinzufügt, eine Zahl markiert und sich die markierte Zahl auf einen bestehenden Wert ändert kann es auch sein dass sich der SelectedIndex ändert.

Ich habe das Problem in meinen Fall so gelöst dass ich eine Klasse gemacht habe welche nur aus einer Struktur besteht. Somit sind bei gleichen Int-Werten trotzdem unterschiedliche Objekte in der Liste.

Hatte schon mal jemand das Problem?
Wie geht ihr damit um? Ist das ein Bug in WPF?

25.08.2009 - 17:33 Uhr

Ohne mir deinen ganzen Beitrag durchgelesen zu haben tu ich dir mal den gefallen und guck in Blend nach wie die Default-Templates aussehen.

Also dein Template würde ich entsprechend verändern:

Buttons raus.
Dem Scrollviewer ein eigenes Template geben. Das hier ist das Default-Template:


<ControlTemplate x:Key="ScrollViewerControlTemplate1" TargetType="{x:Type ScrollViewer}">
			<Grid x:Name="Grid" Background="{TemplateBinding Background}">
				<Grid.ColumnDefinitions>
					<ColumnDefinition Width="*"/>
					<ColumnDefinition Width="Auto"/>
				</Grid.ColumnDefinitions>
				<Grid.RowDefinitions>
					<RowDefinition Height="*"/>
					<RowDefinition Height="Auto"/>
				</Grid.RowDefinitions>
				<Rectangle x:Name="Corner" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Column="1" Grid.Row="1"/>
				<ScrollContentPresenter Margin="{TemplateBinding Padding}" x:Name="PART_ScrollContentPresenter" Grid.Column="0" Grid.Row="0" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False"/>
				<ScrollBar Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Cursor="Arrow" x:Name="PART_VerticalScrollBar" Grid.Column="1" Grid.Row="0" ViewportSize="{TemplateBinding ViewportHeight}" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" AutomationProperties.AutomationId="VerticalScrollBar"/>
				<ScrollBar Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Cursor="Arrow" x:Name="PART_HorizontalScrollBar" Grid.Column="0" Grid.Row="1" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Value="{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" AutomationProperties.AutomationId="HorizontalScrollBar"/>
			</Grid>
		</ControlTemplate>

Zusammenfassung: Scrollviewer besteht aus 2 Scrollbars die per TemplatePart eingebunden werden. Sie müssen also PART_Irgendwas heissen. Das gleiche gilt für den ScrollContentPresenter.

Wir verändern dieses Template in etwa so:


<Grid>
<Horizontale Scrollbar im hintergrund/>
<ScrollContentPresenter Margin=16,0,16,0/>
</Grid>

Nun haben wir eine große Scrollbar als Hintergrund im Header. Darüber liegt unser Innhalt. Wenn wir links und rechts platz lassen schauen die beiden Buttons der Scrollbar durch. Da das warscheinlich hässlich ist weisen wir dieser Scrollbar auch noch ein Template zu. Das Default-Template sieht so aus (nur bei Horizontalen Scrollen):


<ControlTemplate TargetType="{x:Type ScrollBar}">
								<Grid SnapsToDevicePixels="true" Background="{TemplateBinding Background}">
									<Grid.ColumnDefinitions>
										<ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
										<ColumnDefinition Width="0.00001*"/>
										<ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
									</Grid.ColumnDefinitions>
									<RepeatButton Style="{StaticResource ScrollBarButton}" Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="LeftArrow" Command="{x:Static ScrollBar.LineLeftCommand}"/>
									<Track x:Name="PART_Track" Grid.Column="1">
										<Track.Thumb>
											<Thumb Style="{StaticResource ScrollBarThumb}" Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="HorizontalGripper"/>
										</Track.Thumb>
										<Track.IncreaseRepeatButton>
											<RepeatButton Style="{StaticResource HorizontalScrollBarPageButton}" Command="{x:Static ScrollBar.PageRightCommand}"/>
										</Track.IncreaseRepeatButton>
										<Track.DecreaseRepeatButton>
											<RepeatButton Style="{StaticResource HorizontalScrollBarPageButton}" Command="{x:Static ScrollBar.PageLeftCommand}"/>
										</Track.DecreaseRepeatButton>
									</Track>
									<RepeatButton Style="{StaticResource ScrollBarButton}" Grid.Column="2" Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="RightArrow" Command="{x:Static ScrollBar.LineRightCommand}"/>
								</Grid>
							</ControlTemplate>

Da du nur die Pfeile brauchst kannst du alles ausser den Repeatbuttons rausnehmen.

Ich hoffe das hilft dir weiter.

25.08.2009 - 14:49 Uhr

Die entsprechenden Commands sind statische Member der ScrollBar-Klasse.
z.B. ScrollBar.LineLeftCommand. Diese kannst du der Command-Eigenschaft der Buttons ja einfach zuweisen.

25.08.2009 - 14:27 Uhr

Ich finde auch dass ein Template für die ListView die beste Möglichkeit ist. Ich habe Beispiele für ein entsprechendes TabControl schon mal irgendwo bei google gefunden.

Für die ListView würde ich einfach so vorgehen:

Schau dir in Snoop mal den VisualTree einer ListView mit Default-Scrollbar mal genauer an.

Dann lässt du dir von Blend mal das Standard-Template der ListView zeigen.
Diese kannst du dann so verwenden dass die Ansicht nicht mehr komplett in einem Scrollviewer steckt sondern nur die Header.
Die Vertikale Scrollbar kannst du dazu ausblenden.
Dann fügst du noch Buttons hinzu welche du mit Scroll-Left und Scroll-Right-Commands bestückst.

25.08.2009 - 14:14 Uhr

Die ermittelte Geschwindigkeit beträgt jetzt tatsäch UNGEFÄHR die reale Geschwindigkeit.
Es gibt jedoch immer spürbare Abweichungen. D.h. wenn man den Scrollbalken loslässt fährt dieser manchmal etwas zu schnell oder zu langsam weiter.

Hier nochmal meine ausprobierten Verfahren zur Geschwindigkeitsermittlung:

1.
Timer starten der alle 100ms die Position des Scrollbalkens abruft und anhand der letzten Position die Geschwindigkeit ausrechnet:


Speed = (Position - m_positionAtLastTimerTick) / (double)TIMER_INTERVAL;

Bei jeder Positionsänderung die Geschwindigkeit berechnen:


int iNow = Environment.TickCount;
if (control.m_ticksAtLastPositionChange != -1 &&
iNow != control.m_ticksAtLastPositionChange)
{
   control.Speed = ((double)e.NewValue - (double)e.OldValue) /
                            (iNow - control.m_ticksAtLastPositionChange);
}
control.m_ticksAtLastPositionChange = iNow;

In beiden Fällen tritt eine ziemlich große Ungenauigkeit auf. (zu schnell und zu langsam). Manchmal fährt der Balken aber auch mit gleicher Geschwindigkeit weiter.

Ich bin für Tipps dankbar.

25.08.2009 - 11:57 Uhr

Evtl irgendwie über Reflection.
Das würde ich dir aber nicht empfehlen. Versuchs mit FindAncestor, VisualTreeHelper.GetParent() oder speichere evtl in deiner Data/ViewModel-Schicht das Parent ab.

25.08.2009 - 10:04 Uhr

Ja ich meine so ein Scrollen wie bei iPhone & Co.

Wenn ich die Geschwindigkeit habe kann ich das Sliden ja einfach über eine Animation realisieren welche dann das Abbremsen übernimmt. Nur leider scheint gerade das Ermitteln der Geschwindigkeit ein Problem darzustellen.
Hier ist mein Beispielcode:


//Am Anfang erstelle ich einen Timer welche die Position ermitteln soll
m_timer = new DispatcherTimer();
m_timer.Interval = TimeSpan.FromMilliseconds(TIMER_INTERVAL);


//Hier meine Timer-Methode
private double m_positionAtLastTimerTick = -1;

private void timer_Tick(object sender, EventArgs e)
{
   if (m_positionAtLastTimerTick != -1)
   {
        Speed = (Position - m_positionAtLastTimerTick) / (double)TIMER_INTERVAL;
   }
   m_positionAtLastTimerTick = Position;
}

//Und im MouseUp mache ich folgendes:


double slideLength = 1000;
double dTo = Position + Speed * slideLength;
DoubleAnimation animation = new DoubleAnimation(dTo,
   new Duration(TimeSpan.FromMilliseconds((dTo - Position) / Speed)));
BeginAnimation(PositionProperty, animation);

Das sollte dazu führen dass die Scrollbar (ohne Abbremsen natürlich) mit gleicher Geschwindigkeit weiterlaufen sollte. Die Animation ist allerdings viel zu langsam. TIMER_INTERVAL beträgt übrigens 100 (in ms).
Gibt es irgendwelche Ansätze wie ich das besser machen könnte?

EDIT: Oben war in der Berechnung ein kleiner Bug. Das Problem warum es immer zu langsam war ist behoben.

24.08.2009 - 13:33 Uhr

Dort ist beschrieben wie ich Windows dazu bringe das Fenster aufblinken zu lassen. Ich möchte jedoch nur mitkriegen wann das Window von sich aus blinkt um einen entsprechenden Effekt selbst nachzuprogrammieren.

24.08.2009 - 13:30 Uhr

Nun mit mehreren Kreisen würde das so gehen:

Doppelklick-Handler aufs Fenster.
Im Handler mehrere Buttons erzeugen (Mit Ellipse als Template).
Buttons irgendwo platzieren.
Beim Klick auf einen Button Popup anzeigen.

Dafür bräuchtest du dann auch nur einen einzigen Klick-Handler der für alle Buttons zuständig ist.

24.08.2009 - 10:47 Uhr

2 Möglichkeiten:

Du definiert eine Instantz deines Converters als Resource und bindest sie im Style dann per StaticResource ein:


<my:VisibilityConverter x:Key="MyConverter"/>

<Style ...>
<Setter Property="Fill"  Value="{Binding Path=IsNew, Converter={StaticResource MyConverter} , ConverterParameter=#488AC7}" />
</Style>

Oder du verwendest die lange Schreibweise von Binding und definierst dir dort deine Instanz:


<Setter Property="Fill">
  <Setter.Value>
    <Binding Path="IsNew" ConverterParameter="...">
      <Binding.Converter>
        <my:VisibilityConverter/>
</alles wieder zu>

24.08.2009 - 10:33 Uhr

Hallo,

Ich habe die Fenster in meiner Anwendung so modifiziert dass sie ein eigenes Aussehen haben. Realisiert habe ich das indem ich einfach den WindowStyle auf None gesetzt und die Titleleiste nachprogrammiert habe. Das funktioniert soweit wunderbar. Jetzt habe ich allerdings ein Problem bei folgendem Szenario:

Der Benutzer öffnet ein modales Fenster und klickt auf ein 'gesperrtes' Fenster darunter. Normalerweise würde die Default-Titelleiste von XP jetzt kurz blinken. Da diese jedoch ausgeblendet ist muss ich das wohl selbst implementieren.

Meine Frage ist nun: Wie bekomme ich mit wenn das Fenster auf sich aufmerksam machen will???

Die Events (De-)Activated, GotFocus habe ich schon ausprobiert.