Laden...
Avatar #avatar-3071.jpg
TripleX myCSharp.de - Member
Student Nürtingen Dabei seit 30.09.2006 328 Beiträge
Benutzerbeschreibung

Forenbeiträge von TripleX Ingesamt 328 Beiträge

09.01.2010 - 20:48 Uhr

Hallo Gemeinde,

da ich gerade mein erstes Projekt nach dem MVVM-Pattern mache habe ich eine Frage zur Model-Schicht. zuvor habe ich einfach (fast) alles in ein Projekt gehauen, weil es einfacher war und man sich nicht so den Kopf zerbrechen musste. Dies wollte ich aber mal ändern und etwas strukturierter vorgehen und habe jetzt für jede Schicht ein eigenes Projekt erstellt (=> Models, ViewModels, Views und noch ne DataAccess). Ich bin bis jetzt auch ganz zufrieden mit dem MVVM-Pattern doch wie gesagt habe ich jetzt ein paar Fragen zur Modelsschicht.

Ich mache mal ein kurzes Beispiel aus meinem TestProjekt:

Sagen wir wir haben ein simples DataSet mit einer Tabelle:
http://img101.imageshack.us/img101/3480/dataset.png

Wie geht man da jetzt weiter vor? Also ich denke man erstellt jetzt folgende Modelklasse:


    public class UserModel : INotifyPropertyChanged
    {
        #region Private Members
        private int _ID;
        private String name; 
        #endregion

        #region Public Properties
        public int ID
        {
            get { return _ID; }
            set { _ID = value; NotifyPropertyChanged("ID"); }
        }
        public String Name
        {
            get { return name; }
            set { name = value; NotifyPropertyChanged("Name"); }
        } 
        #endregion

        #region Constructors
        public UserModel(dsData.UserRow row)
        {
            ID = row.ID;
            Name = row.Name;
        }
        #endregion

        #region INotifyPropertyChanged Implementation
        //...
        #endregion
    }

Jetzt würde ich gerne haben, dass im Programm alle User welche in der Datenbank vorhanden sind angezeigt werden. Dazu brauche ich im ViewModel ja irgendwo eine Liste mit allen Benutzern:

private List<UserModel> Liste
        {
            get {
                List<UserModel> _liste = new List<UserModel>();
                foreach(dsData.UserRow row in dsData.Instance.User.Rows) {
                    Liste.Add(new UserModel(row));
                }
                return _liste;
           }
        }

Ist das alles korrekt soweit?

Jetzt möchte ich aber nicht nur Daten anzeigen, sondern einen Benutzer auch löschen oder hinzufügen können. Wo bastel ich denn dass jetzt hin?

Mein Problem liegt auch darin, dass ich dachte dass man durch dass MVVM-Pattern jede Schicht "beliebig" austauschen kann. Wenn ich aber jetzt anstatt ein DataSet eine anderes Speichermedium verwenden möchte (z.B. eine textDatei 😄) muss ich ja die ViewModels und die Models abändern, da beide nicht mehr funktionieren werden, da es den Typ "dsData" nicht mehr gibt.

Außerdem habe ich, dadurch dass ich die user in eine Liste speichere, keinen wirklichen Nutzen mehr von der Datenbank?

Noch eine andere Frage:
Wenn ich jetzt im Programm Änderungen am User mache, z.B. ich ändere den namen, wird er jetzt direkt auch in die Datenbank übernommen? oder muss ich beim set-accessor der properties im UserModel noch explizit angeben, dass der Wert in der Datenbank ebenso geändert werden soll?

MfG, TripleX

09.01.2010 - 20:10 Uhr

so mache ich dass ja jetzt (glaube) ich auch. Ich sage in App.xaml, welche Views zu welchem Viewmodel gehören. Wenn ich dann eine neue Oberfläche gestalten möchte muss ich dort das selbe machen.

Also ich bin mit meiner Lösung recht zufrieden so, ausser dass ich halt keine 2 Views für ein ViewModel anbieten kann - mal schauen ob ich dass noch irgendwie hinkriege 😃

09.01.2010 - 17:09 Uhr

@Mr. Evil:

das Problem was ich bei deiner Vorgehensweise sehe ist folgendes:
Wenn man 2 verschiedene Oberflächen für das Programm haben möchte, erstelle ich 2 verschiedene Projekte. Bei deiner Vorgehensweise würde es dann Probleme geben, da ich erstens bei den ViewModels beide Projekte referenzieren muss und zweitens der Aufruf der gewünschten View (welche je nach Oberfläche ja anders ist) nicht klappt.

Oder habe ich irgendwo einen Denkfehler? Ich habe bisher noch kein Anwendung mit verschiedenen Oberflächen gemacht - aber was nicht ist kann ja noch werden.

09.01.2010 - 17:02 Uhr

hallo, ich habe gerade ein kleines Problem. Also ich möchte den Header eines GroupBox selber gestalten. Sollte eigentlich kein Problem sein, denkt man. Was ich möchte ist folgendes:


        <GroupBox.Header>
            <DockPanel LastChildFill="True">
                <Image DockPanel.Dock="Left" Source="../images/drive_network.png" Width="16" Height="16" />
                <Image DockPanel.Dock="Right" Source="../images/gear.png" Width="16" Height="16"/>
				<Label Content="Networkdrives" />
            </DockPanel>
        </GroupBox.Header>

zuerst soll ein Bildchen angezeigt werden, daneben der Text "Networkdrives" und ganz rechts möchte ich wieder ein Bildchen haben (wird später durch ein button ersetzt). Aber ich krieg das einfach nicht hin, dass das Bildchen ganz rechts angezeigt wird.

Irgendwelche Vorschläge?

08.01.2010 - 21:02 Uhr

Ich hätte da eine Idee - wenn du aber mittels dem MVVM-Pattern arbeitest komplizierter zum einbauen wird.

Als du könntest das TextBox.GotFocus()-Event abonnieren und dort das Binding entfernen. Dann musst du noch das LostFocus()-Event abonnieren und dort das Binding wieder hinzufügen.

Ist glaube nicht gerade elegant aber auf die Schnelle fällt mir nichts besseres ein 😃

Btw, Mode=TwoWay ist standard also du brauchst das nicht explizit zu schreiben

MfG TripleX

08.01.2010 - 20:40 Uhr

sollte glaub mittels Window.PreviewKeyDown machbar sein?

08.01.2010 - 16:59 Uhr

Hallo,
ich habe zurzeit ein kleines Problem:

View
Hier möchte ich Label mit einem Bild darstellen:


        <StatusBar DockPanel.Dock="Bottom">
            <StatusBarItem>
                <Label Content="{Binding StatusText}" 
                       controlsAP:AttachedImage.ImageSource="{Binding StatusImage}"
                   	   Style="{DynamicResource ImageLabel}"
                       />
            </StatusBarItem>
        </StatusBar>

ViewModel:
Das ViewModel für die View

  
        #region Public Properties
        public String StatusText { get; private set; }
        public ImageSource StatusImage { get; private set; }
        #endregion
       //...

        private void OpenVPN_StateChanged(object sender, OpenVPN.OpenVPNStateEnum newState)
        {
              //...
                    case OpenVPNStateEnum.EXITING:
                    case OpenVPNStateEnum.DISCONNECTED:
					default:
                        StatusImage = new BitmapImage(new Uri(image_dir + "flag_red.png", UriKind.Relative));
                        StatusText = "Disconnected from server";
                        break;
                }
                NotifyPropertyChanged("StatusImage");
                NotifyPropertyChanged("StatusText");

LabelStyles:
Das Style für das Label mit Bild

    <Style x:Key="ImageLabel" TargetType="{x:Type Label}">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image  Source="{Binding Path=(ControlLibAP:AttachedImage.ImageSource), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Label}}}"
                                HorizontalAlignment="Left"
                                Height="16"
                                Width="16" />
                        <TextBlock  Text="{TemplateBinding Content}"
                                    Margin="5,0,0,0"
                                    HorizontalAlignment="Center" />
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Das Problem ist jetzt, dass das Bild nicht angezeigt wird. Wenn ich anstatt Binding aber den korrekten Pfad angebe, dann funktioniert es und das Bild wird richtig angezeigt. Wo habe ich denn was vergessen??

Mfg TripleX

07.01.2010 - 16:47 Uhr

Probiere einfach mal folgendes Beispiel aus diesem Link aus: http://www.hardcodet.net/2009/01/create-wpf-image-button-through-attached-properties . Dort habe ich das gleiche Problem wie bei meinem jetzigen.

Aber ich glaube wirklich dass es an VS2010 liegt, denn wenn ich das Projekt mittels Blend 4 lade funktioniert es ohne Fehlermeldung und ohne dauernde Abstürze, doch mir gefällt zum Programmieren dann doch das Visual Studio mehr 😉

07.01.2010 - 03:55 Uhr

Wäre sehr nett wenn jemand das Beispiel aus dem oben genannten Link testen könnte, und berichten ob bei ihm in designer eine Exception auftritt. Dann könnte man das Problem vielleicht auf VS2010 (is ja noch beta ...) eingrenzen

danke 😃

06.01.2010 - 12:21 Uhr

ich verwende Visual Studio 2010 Beta 2

06.01.2010 - 04:20 Uhr

Hallo,

also was mir bei deinem Code auffällt ist die 5te Zeile. Also 1. gibt es bei mir string.LongCount() gar nicht und 2tens erhält man die Länge eines Strings viel einfach mittels string.Length.

Also wenn ich dich richtig verstanden habe, hast du ein Wort und ein Buchstabe. Jetzt willst du wissen, wie oft der Buchstabe in dem Wort vorkommt:

            string wort = "FooBar";
            string buchst = "o";
            int anzahl = 0;

            if (wort.Contains(buchst))
            {
                foreach (char zeichen in wort)
                {
                    if (zeichen.ToString() == buchst) anzahl++;
                }
            }

Btw, fast jedes objekt bietet dir eine .ToString() Methode an, ist ganz nützlich, dann brauchst du nicht (wie in der letzte Zeile bei dir) die Convert.ToString()-Methode verwenden.

06.01.2010 - 03:58 Uhr

Ich kriege zur Zeit immer wieder folgende Fehlermeldung im Designer:

System.FormatException
Prefix 'controlsAP' does not map to a namespace.
   at System.Xaml.Schema.XamlTypeName.Parse(String typeName, IXamlNamespaceResolver namespaceResolver)

Das Programm ansich läuft aber ganz normal. Mich nervt es nur sehr, da seit der Fehler kommt mein Visual Studio immer wieder abstürzt. Ich habe in den letzten paar Stunden mein Visual Studio mindestens 100x neustarten dürfen (kein Witz!!)

Also ich konnte das Problem eingrenzen und das Problem liegt hier:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:controlsAP="clr-namespace:ControlLibrary.AttachedProperties;assembly=ControlLibrary"
                    >
    
    <!--  A button style that displays an attached image -->
    <Style x:Key="ImageButton" TargetType="{x:Type Button}">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Grid>
                        [B]<Image  Source="{Binding Path=(controlsAP:AttachedImage.ImageSource), 
                                                 RelativeSource={RelativeSource FindAncestor, 
                                                                                AncestorType={x:Type Button}}}"[/B]
                                HorizontalAlignment="Left"
                                Margin="8,0,0,0"
                                Height="16"
                                Width="16" />
                        <TextBlock  Text="{TemplateBinding Content}"
                                    HorizontalAlignment="Center" />
                    </Grid>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Ich habe die Zeile, wo es Probleme gibt, Fett gedruckt. Doch ich hab kein Plan wie sich das behebn lässt. Achja, den Code habe ich von hier "kopiert":
http://www.hardcodet.net/2009/01/create-wpf-image-button-through-attached-properties

Wenn ich da dass Beispiel starte tritt das gleiche Problem ebenfalls auf.

Ich hoffe ihr könnt mir weiterhelfen, denn so macht das Pogrammieren kein Spaß, wenn man jede Minute VS neustarten darf 😭

05.01.2010 - 23:02 Uhr

öhm eigentlich habe ich bereits alles gepostet - mehr Code gibt es nicht. Schreib doch mal was du bisher versucht hast

05.01.2010 - 22:02 Uhr

ist eigentlich ganz einfach. Du brauchst im CodeBehind des Windows eine property:

        private String _text;
        public String Text { 
            get { return _text; } 
            set { _text = value; NotifyPropertyChanged("Text"); } 
        }

Im XAML-Code schreibst du dann zum Beispiel folgendes:

<TextBox Text="{Binding Text}" />

Damit das Binding funktioniert, musst du noch den DataContext setzen, am Besten im Konstruktor:

public Window1() {
       this.DataContext = this;
}

schau dir auch mal folgenden webcast an: http://www.microsoft.com/germany/msdn/webcasts/library.aspx?id=1032383898

05.01.2010 - 21:47 Uhr

btw, so gehts au:

            bool userInGroup = (from user in groups.Users
                                where user.ID == ID
                                select user).Count() > 0;

weil man den Code halt doch mehrfach braucht ...

Du könntest dir eine Funktion in der Klasse Groups schreiben "bool HasUser(int ID)" welche das Gewünschte implementiert

05.01.2010 - 21:41 Uhr

probiers mal folgendermaßen:

int ID = XX;
bool isUserInGroup = groups.Users.Exists(x => x.ID == ID);
05.01.2010 - 15:36 Uhr

wäre es hier nicht einfacher die DataType - Property des DataTemplates zu verwenden ?

Das war auch mein erster Versuch, doch ich habe es nicht geschafft an die Datentypen zu kommen. Aber Ich habe es jetzt eh anders gelöst und zwar habe ich einen Converter geschrieben:

    public class IsDataSetRowConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Type type = null;

            if (parameter == null) return null;
            else if ((string)parameter == "Category") type = typeof(dsData.dtCategoriesRow);
            else if ((string)parameter == "Feed") type = typeof(dsData.dtFeedsRow);

            if (value is TreeViewItem)
            {
                TreeViewItem tvi = value as TreeViewItem;
                return tvi.Header.GetType() == type;
            }
            else return value.GetType() == type;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

und das XAML für die treeView sieht jetzt folgendermaßen aus:

        <TreeView ItemsSource="{Binding RootItems}">
            <TreeView.Resources>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="HeaderTemplate">
                        <Setter.Value>
                            <DataTemplate DataType="ContentPresenter">
                                <StackPanel Orientation="Horizontal">
                                    <Image Name="img" Width="16" Height="16" Stretch="Fill" />
                                    <TextBlock Text="{Binding Name}" Margin="5,0" />
                                </StackPanel>

                                <DataTemplate.Triggers>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}},
                                                                   Converter={StaticResource IsDataSetRowConverter},
                                                                   ConverterParameter=Category }" 
                                                 Value="true">
                                        <Setter TargetName="img" Property="Source" Value="../images/folder.png" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}},
                                                                   Converter={StaticResource IsDataSetRowConverter},
                                                                   ConverterParameter=Feed }" 
                                                 Value="true">
                                        <Setter TargetName="img" Property="Source" Value="../images/feed.png" />
                                    </DataTrigger>
                                </DataTemplate.Triggers>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TreeView.Resources>

Soweit funktioniert das auch, aber nicht zufriedenstellend, denn ich weiß einfach nicht wie ich die Children der jeweiligen Items anzeigen lassen soll.

Also das Problem ist folgendermaßen:

Ich habe ein DataSet mit 2 Tabellen - siehe Anhang für Bild. Jetzt möchte ich diese 2 Tabellen in ein TreeView anzeige lassen. Und wenn das Item eine "CategorieRow" ist sollte vor dem TreeViewItem ein kleines Bildchen mit einem Ordner angezeigt werden - andersrum möchte ich ein rss-bildchen anzeigen lassen wenn das Item eine FeedRow ist.

Wie würdet ihr sowas machen? Die Lösung soll ganz nachm MVVM-Pattern implementiert werden.

Mal ne andere Frage: Wenn ich ein DataSet als Datenquelle habe (und das sich auch nicht ändern wird), sollte ich dann trotzdem für jede Tabelle eine Modelklasse (wie es dass MVVM-Pattern vorsieht) erzeugen?

05.01.2010 - 13:45 Uhr

du meinst sicherlich dass die **View **keine ViewModels eigenständig instanziiert - oder?

04.01.2010 - 23:07 Uhr

danke für das Stichwort. Falls jemand das gleiche Problem wie ich hat, der soll sich mal folgenden Link anschauen:

http://www.switchonthecode.com/tutorials/wpf-tutorial-how-to-use-a-datatemplateselector

04.01.2010 - 22:09 Uhr

Hallo Gemeinde,

ich wollte mal fragen ob folgendes möglich ist: Angenommen man hat 2 Klassen (Category und Item), welche man an ein TreeView binden möchte.

Jetzt wollte ich im TreeView das aussehen der Items je nach Datentyp festlegen. Zum Beispiel sollte beim Typ "Category" ein Bild mit angezeigt werden können, beim Typ "Item" sollte keines angezeigt werden.

MfG TripleX

04.01.2010 - 16:37 Uhr

ganz einfach

  
<Views:MyControlView Grid.Row="1" DataContext="{Binding MyControlViewModel}" />  
  

mal ne kurze Anmerkung von mir. Ich setze den DataContext bisher immer im XAML der jeweiligen View:

    <Window.DataContext>
        <VM:DialogViewModel />
    </Window.DataContext>

Jetzt habe ich gesehen dass du dass anders machst, und ich glaube deine Vorgehensweise ist besser, da es flexibler ist. Danke *g

04.01.2010 - 00:47 Uhr

dein letzter part ist doch genauso wie das original service provider es vor sieht

Sprichst du gerade vom ServiceProvider oder vom DialogService?

du arbeitest drumrum und fummelst sachen dazu - am ende hast du das selbe ergebnis...

ja, aber dafür habe ich es selber gemacht und was dazugelernt. Ich mag nicht einfach Code kopieren, wo ich dann selber kein plan hab warum der Code so ist wie er ist 😃 Ich schaue mir nur anderen Code an, um Ideen zur Implementierung zu erhalten.

bei meiner library ist der aufruf so: [....]

Ist ja ähnlich wie mein Code, nur der Aufruf des Dialogs ist bei dir vereinfacht. Werde das bei mir auch noch umändern - weil so wie es jetzt ist, ist es eindeutig zu lang.

ich bevorzuge meine loesung da man dort nichts in der xaml machen muss

Ich muss auch nichts in xaml machen, außer irgendwo den Command binden - was du auch machen musst. Achja ausserdem muss ich dem DialogService die Views bekannt machen, du nicht da du beim Aufruf des Dialogs die View direkt mitgibst. das wollte ich nicht, da es ja eine Regel bei MVVM gibt: "ViewModel knows about the Model but not the View" und "ViewModel does not need a reference to a View"

Ich finde meine Lösung auch nicht wirklich schön, aber ich habe keine bessere alternative gefunden - ausser ich würde meinen ViewModels erlauben, auf die Views zuzugreifen, dann könnte cih mir das ganze registrieren und speichern der Views nämlich sparen.

weil du den original service provider nach programmiert hast
=> nur aufwaendiger (oder ist der originale tatsaechlich so aufwaendig?)

Wie schon oben gefragt - sprichst du vom ServiceProvider oder vom DialogService - weil den Code vom ServiceProvider habe ich nirgends gepostet. Und den Code vom DialogService finde ich gar nicht so aufwendig, bei "Cinch" ist er viel aufwendiger.

die probleme die ich bei deiner loesung sehe:

  • du hst keinen owner gesetzt
  • du kannst keine zwei unterschiedlichen views fuer ein viewmodel haben

Du hast bei beiden Punkten vollkommen recht - und die sachen sind mir auch während der Programmierung aufgefallen.
Die Sache mitm owner wollte ich noch einbauen. Beim 2ten Punkt war ich mir nicht wirklich sicher, ob es in der Praxis vorkommt, dass man 2 Views für ein ViewModel hat (oder umgekehrt)

Jedenfalls bedanke ich mich hier bei allen Poster, ihr habt mir alle sehr weitergeholfen und ich hab viel dazugelernt (was dass wichtigste ist).

MfG TripleX

03.01.2010 - 02:43 Uhr

Evtl. so was in der Art: ...

funktioniert auch nicht, bei folgendem Code gibt er mit für dir=x86:

string dir = "x" + (Marshal.SizeOf(typeof(IntPtr)) == 8 ? 64 : 86);

EDIT: Wegen deinem Problem das es falsch angezeigt wird, überprüf mal was du bei den Projekteigenschaften bei release und im debug mode als zielplattform eingestellt hast. Habs jetzt nciht überprüft könnte aber damit zusammenhängen

jo das wars ... danke dir 😃

03.01.2010 - 00:55 Uhr

Hallo Gemeinde,

um herauszufinden ob ein Benutzer einen 64Bit oder einen 32Bit Prozessor bzw. Betriebssystem hat, verwende ich folgenden Code:

                string pa = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
                string dir = "x" + ((String.IsNullOrEmpty(pa) || String.Compare(pa, 0, "x86", 0, 3, true) == 0) ? 86 : 64);

Nur leider funktioniert der nicht, denn er meint ich hätte einen 32Bit Prozessor, obwohl ich einen 64 Bit habe.

Wenn ich die Kommandozeile aufrufe und dort die Umgebungsvariable "PROCESSOR_ARCHITECTURE" auslese, wird korrekterweise PROCESSOR_ARCHITECTURE=AMD64 ausgegeben. Wenn ich mir die variable "pa" ausgeben lasse im Debugger, steht aber "x86" drin. Nun wollte ich fragen wo der Fehler ist, oder ob jemand eine andere Möglichkeit hat um zu schauen ob ein Benutzer mit 64 oder 32 Bit unterwegs ist.

MfG TripleX

02.01.2010 - 23:25 Uhr

Also ich habe mich heut mal wieder an den Dialogen versucht und ich habe jetzt (endlich) geschafft, einen Dialog anzuzeigen. Ich habe jetzt folgendes gemcaht:

ich habe einen ServiceProvider implementiert, welche 3 Methoden besitzt:*AddService *Removeservice *GetService

Außerdem habe cih einen DialogService programmiert, welche wie folgt aussieht:

    public class DialogService : IDialogService
    {
        #region Private Members
        private Dictionary<Type, Type> registeredViews;
        #endregion

        #region Constructors
        public DialogService()
        {
            registeredViews = new Dictionary<Type, Type>();
        }
        #endregion

        #region IDialogService Implementation
        public void Register<TViewModel, TView>()
            where TViewModel : ViewModelBase
            where TView : Window
        {
            if (registeredViews.Keys.Contains(typeof(TViewModel))) throw new ArgumentException("View has already been registered.");
            else
            {
                registeredViews.Add(typeof(TViewModel), typeof(TView));
            }
        }
        public void UnRegister<TViewModel>() where TViewModel : ViewModelBase
        {
            if (!registeredViews.Keys.Contains(typeof(TViewModel))) throw new ArgumentException("View has never been registered.");
            else
            {
                registeredViews.Remove(typeof(TViewModel));
                Debug.WriteLine("DialogService.UnRegister(" + typeof(TViewModel) + ")");
            }
        }
        public bool? ShowDialog(ViewModelBase viewModel)
        {
            if (!registeredViews.Keys.Contains(viewModel.GetType())) throw new ArgumentException("View has never been registered.");
            else
            {
                Type winType = registeredViews[viewModel.GetType()];
                Window win = (Window)Activator.CreateInstance(winType);
                win.DataContext = viewModel;
                return win.ShowDialog();
            }
        }
        public bool? ShowDialog<TViewModel>() where TViewModel : ViewModelBase
        {
            if (!registeredViews.Keys.Contains(typeof(TViewModel))) throw new ArgumentException("View has never been registered.");
            else
            {
                Type winType = registeredViews[typeof(TViewModel)];
                Window win = (Window)Activator.CreateInstance(winType);
                return win.ShowDialog();
            }
        }
        #endregion
    }

Jetzt muss ich natürlich den DialogService zum ServiceProvider hinzufügen und die Views + VieModels den DialogService bekannt geben. Dies habe ich im CodeBehind der App.xaml gemacht:

            #region Add services to Service Provider
            ServiceProvider.AddService<IDialogService>(new DialogService());

            IDialogService dialogService = ServiceProvider.GetService<IDialogService>();
            dialogService.Register<DialogViewModel, Views.DialogView>();
            #endregion

Zum testen habe ich in der MainWindowView einen Button implementiert, welcher an ein kommando gebunden ist:

        #region ShowDialogCommand
        private SimpleCommand showDialogCommand;
        public ICommand ShowDialogCommand
        {
            get
            {
                if (showDialogCommand == null)
                {
                    showDialogCommand = new SimpleCommand(param => ExecuteShowDialogCommand(), param => this.CanExecuteShowDialogCommand);
                }
                return showDialogCommand;
            }
        }

        public bool CanExecuteShowDialogCommand
        {
            get { return true; }
        }
        private void ExecuteShowDialogCommand()
        {
            DialogViewModel vm = new DialogViewModel();
            vm.Name = "Test";
            ServiceProvider.GetService<IDialogService>().ShowDialog(vm);
            ServiceProvider.GetService<IDialogService>().ShowDialog<DialogViewModel>();
        }
        #endregion

Wenn ich jetzt auf den Button drücke wird mein Dialog angezeigt - genau so wie ich wollte 😃

Jetzt wollte ich fragen ob das so in Ordnung ist, oder ob ich irgendetwas wichtiges übersehen habe?

Viele Grüße, TripleX

26.12.2009 - 21:19 Uhr

hm ist echt ein komplexes Thema .. ich versuche schon seit ner Woche irgendwie das ganze zu implementieren.
Sagen wir mal wir haben ein (fiktives) Programm wo man Namen von Personen zu einer Datenbank hinzufügen kann. Nun gibt es ja 2 Möglichkeiten den Namen einzugeben:

  • über einen Button. Wenn man drauf klickt soll ein neues Fenster erscheinen, wo der name der Person abgefragt wird.
    oder
  • im hauptprogramm gibt es eine TextBox wo man einen Namen eingeben kann.

Ich möchte mir gerne beide Möglichkeiten offenhalten in meinem Programm, d.h. an den ViewModels sollte nichts geändert werden, da dass ganze ja eine Designsabhängig ist.

Deswegen hab ich mir gedacht, ich packe die oben genannte Funktionalität in ein neues UserControl. Im AddPersonView kommt dann ein label, eine textbox und ein button rein. Außerdem gibts eine AddPersonViewModel, wo es iene Property "Name" und einen Command "AddPersonCommand" gibt.

Im View des Hauptfensters kann man ja dann bestimmen, ob "AddPersonView" direkt angezeigt wird, oder sich ein neues Fenster öffnen soll. Das Anzeigen ist ja easy, nur das ganze in einem neuen Dialog zu öffnen bereitet mir Schwierigkeiten, wenn man keinen Code im Code-Behind haben möchte.

Also was ich bis jetzt habe ist folgendes:
ShowDialog.cs

    [ContentProperty("View")]
    public class ShowDialog : System.Windows.Controls.SoundPlayerAction
    {
        public FrameworkElement View { get; set; }

        public ShowDialog()
        {
            Debug.WriteLine("ShowDialog.ShowDialog()");
        }
    }

XAML Code: (dass sollte der Aufruf des Dialogs werden ...)

        <Button Content="Show Dialog">
            <Button.Style>
                <Style>
                    <Style.Triggers>
                        <EventTrigger RoutedEvent="Button.Click">
                            <EventTrigger.Actions>
                                <Services:ShowDialog>
                                    <views:DialogView />
                                </Services:ShowDialog>
                            </EventTrigger.Actions>
                        </EventTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>

Problem dabei ist jetzt, dass der Dialog, der angezeigt werden soll, direkt beim Start initialisiert wird, und sich dadurch das Programm nicht richtig beendet. Außerdem wird beim Klick auf den Button der Dialog noch nicht angezeigt - kein Wunder, ich ruf ja nirgends die Funktion *.ShowDialog() auf.

Zum Schluss noch ein paar Fragen:

  • Wie kann ich verhindern dass der Dialog beim Start initialisiert wird
  • Wie kann ich den Dialog anzeigen lassen
  • Ich war nicht in der Lage, den XMAL-Code in folgender Weise zu schreiben: <Services:ShowDialog View="...">. Das muss doch irgendwie machbar sein ... oder?

Bin für jede Kritik oder jeden Hinweis dankbar

MfG TripleX

26.12.2009 - 03:49 Uhr

Hallo Gemeinde,

ich habe gerade ein kleines Problem und kommt gerade nicht weiter. Also ich möchte nach einem Klck auf einen Button ein (eigener) Dialog anzeigen. Es soll also ein neues Fenster geöffnet werden welcher den Inhalt einer View entspricht.

Ich habe mich schon seit längerer Zeit eingelesen und habe keine wirklich zufriedenstellende Lösung gefunden. Also meine Idee war folgende:

In mein MVVM-Framework habe ich eine DialogService, wo ich jede View über eine AttachedProperty (IsRegisteredView) registriere und in eine Liste speichere (siehe http://www.codeproject.com/KB/architecture/MVVM_Dialogs.aspx).
Das klappt auch soweit ganz gut. Nun wollte ich in einer View mittels Button-Click einen Dialog aufrufen. Dies wollte ich jetzt mit einem EventTrigger lösen, beispielsweise so:

        <Button Content="Show Dialog">
            <Button.Style>
                <Style>
                    <Style.Triggers>
                        <EventTrigger RoutedEvent="Click">
                            <EventTrigger.Actions>
                                <Services:DialogService.ShowDialog(views:DialogView) />
                            </EventTrigger.Actions>
                        </EventTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>

jaja, der Code funktioniert nicht - ich weiss. Es sollte nur dazu dienen, meinen Überlegung zu verdeutlichen.

meine Frage lautet nun, ob dass so überhaupt machbar ist? Mein Problem ist halt, dass ich den Aufruf des Dialog aufjedenfall im View haben möchte. ich habe mehrere Beispiele gesehen, wo der Dialog im ViewModel aufgerufen wurde, dass gefällt mir aber überhaupt nicht, da so eine Dialoggeschichte eher zum Design gehört und meines Erachtens eben in die View gehört.

€dit:
hab gerade bemerkt dass das Registrieren der View eigentlich unnötig ist, da mein Dialog ja gar nicht registriert wird ... (da es noch nicht initialisiert wurde am Anfang). Hätte ich eigentlich wissen müssen 😭. Naja, vielleicht brauche ich dass ja auch gar nicht sondern ich öffne beim Aufruf von DialogService.ShowDialog(views: DialogView) "einfach" irgendwie den Dialog, die View habe ich ja dann. Nur bleibt die Frage -> wie ??

MfG TripleX

23.12.2009 - 20:26 Uhr

danke talla, werde mich dann über die Semesterferien mal hinsetzen und dass ganze mal mit Attached Propperties probieren.

Btw, ich seh gerade dass du aus der Stadt kommst, wo ich studiere 8)

Liebe Grüße,
TripleX

22.12.2009 - 20:20 Uhr

danke für den Link. Also ich habe mir das ganze mal angeschaut und mach mal en kurzes Statement:

Also die Vorteile meiner Vorgehensweise (custom control) im Gegensatz zu den APs wären:

  • Ich muss nicht in jedem Projekt ein ControlTemplate für die jeweiligen Controls (ImageButton, ImageLabel, ...) erstelllen, sondern muss nur meine Library einbinden und könnte direkt mittels <ControlLib:ImageButton ...> darauf zugreifen.

  • Der XAML-Code wird kürzer, da ich das Controltemplate nicht brauche und weil ich mir dann WpfApplication1:ButtonExtensions.AdditionalText="0" sparen kann, ich würde einfach nur AdditionalText="0" schreiben

Die Vorteile deiner Vorgehensweise wären:

  • Ich müsste nur einmal eine Klasse schreiben, welche die von mir gewünschten DPs besitzt und könnte die dann für jedes(?) Control verwenden.
  • Die Gestaltung der Buttons ist flexibler durch das ControlTemplate

Also ansich überwiegen die Vorteile deiner Vorgehensweise. Das einzige was mich halt wirklich stören würde, ist dass der XAML-Code unnötig aufgeblasen wird. Außerdem fällt mir kein guter Name für die Klasse ein 👅.

21.12.2009 - 21:35 Uhr

Aber die drei Controls sind doch im Grunde das gleiche. Warum verwendest du nicht einfach ein paar attached Properties mit einem Style?

Da hast du recht, sie sind im Grunde das gleiche. Man könnte es bestimmt mit AttachedProperties lösen, doch ich würde es bevorzugen ein eigenes Control zu verwenden, weil es einfacher ist zu verwenden? Habe aber noch kein eigenes Attached Property programmiert, sollte ich mir aufjedenfall mal anschauen (man lernt ja nie aus).

Ansonsten siehst du keine bessere Möglichkeit, das Problem zu umgehen?

ansonsten halt die gemeinsame Funktionalität in eine Klasse hauen, und in Deinen Controls einen Member und diesen entweder wrappen oder - was weniger pflegeintensiv ist wenn sich was ändert - diesen Member über ein public Property nach außen sichtbar machen.

Daran habe ich auch schon gedacht. Ich könnte zum Beispiel ein Property vom Typ Image und eines vom Typ TextBlock in den jeweiligen Klassen setzen, und könnte dann im XAML mittels <ImageButton Image.ImageSource="..." TextBlock.Text="..."> auf die Properties zugreifen. Doch ich weiß nicht ob dass wirklich ein sauberer Programmierstil ist *g

21.12.2009 - 21:06 Uhr

hallo Gemeinde,

ich bastel mir gerade eine ControlLibrary für Wpf und ich habe 3 Controls, welche fast identisch sind.

Also ich habe einmal ein ImageLabel, ImageButton und ImageToggleButton. Alle 3 Controls besitzen die gleichen Dependency Properties wie: ImageWidth, ImageHeight, ImageSource, ImageVisibility, Text, TextVisibility, usw. und haben den gleichen Konstruktor. Der einzige Unterschied ist eben dass die Klasse anders heißt und sie von verschiedenen Klassen abgeleitet sind.

Nun wollte ich fragen ob ich das ganze nicht irgendwie vereinfachen kann, damit ich wenn ich z.B. mal ein neues Property hinzufüge dass nicht in allen 3 klassen ändern muss. Mehrfachvererbung in c# ist ja nicht möglich - ich weiß - und Interfaces werden mir da auch nicht weiterhelfen ... Aber dass muss doch irgendwie geschickter gehen 👅

MfG TripleX

P.S. sry für den blöden ThreadTitel, mir ist nichts besseres eingefallen

21.12.2009 - 20:40 Uhr

Stimmen die Datentypen überein?

sollten glaub ich passen, dass binden geht ja zumindest 🙂

Meinst du eventuell ImageSource.Width?

Nein, ich meine schon Image.Width. Und das Problem besteht auch bei weiteren Kommentaren, wo ich z.B. <see cref="TextBlock.Margin"> oder <see cref="TextBlock.Visibility"> verwende.

Vielleicht ist das auch einfach nur ein Fehler von Visual Studio 2010, aber mich nervst halt gewaltig, wenn da in meinem Projekt 38 Warnungen auftreten 🙁

€dit: Bin mal mit der Maus, über die Properties vom Element, welches "gebinded" werden soll gefahren, und siehe da - Width und Margin sind gar nicht vom Typ Image / TextBlock, sondern vom Typ FrameWorkElement ... also hab ich das mal umgeändert und jetzt gibts keine Probleme mehr.

Trotzdem vielen Dank euch beiden

21.12.2009 - 20:30 Uhr

habs nur mal ganz kurz überflogen, und konnte nirgends ein "DataContext" finden. Hast du das vielleicht vergessen?_

21.12.2009 - 09:48 Uhr

Hallo Gemeinde,

ich hätte da eine kurze Frage zu den XML-Kommentaren (in WPF, aber das ist Nebensache). also wenn ich folgendes schreibe:

/// <summary>
        /// Gets or sets the <see cref="Image.Source"/> for the <see cref="Image"/>
        /// </summary>
        [Category("Common"), Description("Gets or sets the System.Windows.Media.ImageSource for the image")]
        public ImageSource ImageSource
        {
            get { return (ImageSource)GetValue(ImageSourceProperty); }
            set { SetValue(ImageSourceProperty, (ImageSource)value); }
        }

Damit möchte ich sagen, dass die Eigenschaft eben die Source-Property für das Image-Element setzt. Dass funktioniert primae und der Compiler zeigt mir keine Fehlerwarnung an.

Wenn ich aber folgendes schreibe:

        /// <summary>
        /// Gets or sets the <see cref="Image.Width"/> of the <see cref="Image"/> element
        /// </summary>
        [Category("Layout"), Description("Gets or sets the width of the element")]
        public double ImageWidth
        {
            get { return (double)GetValue(ImageWidthProperty); }
            set { SetValue(ImageWidthProperty, (double)value); }
        }

Dann sagt der Compiler dass " ... 'Image.Width' cannot be resolved". Wo liegt denn da der Fehler?

MFG TripleX

21.12.2009 - 09:43 Uhr

Sieht gut aus, danke 😃

20.12.2009 - 23:38 Uhr

Hallo Gemeinde,

ich habe folgenden Button:

<ControlLib:ImageToggleButton Name="btnAutoScroll"
                                                  TextVisibility="Collapsed"
                                                  ImageSource="../images/page_down.gif"
                                                  >
                        <ControlLib:ImageToggleButton.Style>
                            <Style TargetType="{x:Type ControlLib:ImageToggleButton}">
                                <Style.Triggers>
                                    <Trigger Property="IsChecked" Value="True">
                                        <Setter Property="ToolTip" Value="Deactivate Autoscroll" />
                                    </Trigger>
                                    <Trigger Property="IsChecked" Value="False">
                                        <Setter Property="ToolTip" Value="Activate Autoscroll" />
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </ControlLib:ImageToggleButton.Style>
                    </ControlLib:ImageToggleButton>

Ziel ist es, die ToolTip Eigenschaft zu ändern, wenn der ToggleButton Checked ist. soweit funktioniert das auch, doch ich habe mir überlegt dass es Probleme geben könnte, wenn man ein globales Style auf diesen Button anwenden möchte. Zum Testen habe ich das einfach mal versucht:


    <UserControl.Resources>
        <Style x:Key="myStyle" TargetType="Button">
            <Setter Property="Background" Value="Orange" />
        </Style>
    </UserControl.Resources>
<ControlLib:ImageToggleButton Name="btnAutoScroll"
                                                  TextVisibility="Collapsed"
                                                  ImageSource="../images/page_down.gif"
                                                  Style="{StaticResource myStyle}"
                                                  > 
...

Wie ich schon gedacht habe gibt es dadurch Probleme, da ich das Style-Element zweimal angegeben habe.
Nun würde ich gerne wissen, wie man so ein Problem beheben könnte 😃

MfG TripleX

20.12.2009 - 21:53 Uhr

und für die Nachwelt stelle ich mal den korrekten Code online zur Verfügung:

/// <summary>
    /// This forces a <see cref="TextBoxBase"/> to scroll to end automatically when the <see cref="TextBoxBase.TextChanged"/>-Event is fired
    /// <example>
    /// <![CDATA[
    ///     AttachedBehaviour:AutoScrollTextBoxBaseBehaviour.IsEnabled="True"
    /// ]]>
    /// </example>
    /// </summary>
    public static class AutoScrollTextBoxBaseBehaviour
    {
        #region Dependency Properties
        #region IsEnabledProperty
        /// <summary>
        /// Dependency-Property for enabling the autoscroll-feature
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(AutoScrollTextBoxBaseBehaviour), new UIPropertyMetadata(false, IsEnabledChangedCallback));
        /// <summary>
        /// Attached Property getter for the IsEnabled property.
        /// </summary>
        public static bool GetIsEnabled(DependencyObject source)
        {
            return (bool)source.GetValue(IsEnabledProperty);
        }
        /// <summary>
        /// Attached Property setter for the IsEnabled property.
        /// </summary>
        public static void SetIsEnabled(DependencyObject source, bool value)
        {
            source.SetValue(IsEnabledProperty, value);
        }
        /// <summary>
        /// The property changed handler for the IsEnabled property.
        /// </summary>
        private static void IsEnabledChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            TextBoxBase tbb = sender as TextBoxBase;
            if (tbb == null) return;

            tbb.TextChanged -= OnTextChanged;

            bool b = ((e.NewValue != null && e.NewValue.GetType() == typeof(bool))) ? (bool)e.NewValue : false;
            if (b)
            {
                tbb.TextChanged += OnTextChanged;
            }

        }
        #endregion
        #endregion

        #region Private Methods
        /// <summary>
        /// If <see cref="TextBoxBase.TextChanged"/>-Event is fired, scroll to the end
        /// </summary>
        private static void OnTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            ((TextBoxBase)sender).ScrollToEnd();
        }
        #endregion
    }

Ein Problem gibts zwar noch - es gibt keine IntelliSense-Hilfe - aber Hauptsache es funktioniert *g

20.12.2009 - 21:45 Uhr

vergiss Intellisense. Das 2008er Intellisense kann lange nicht alles was in XAML möglich ist.

ich verwende Visual Studio 2010 (Beta 2), da is IntelliSense viel schlauer geworden 😉

Und was die Benennung der Namen angeht musst du unbedingt so vorgehen. Bei attached Properties brauchst du immer die statischen Get und Set Methoden und bei normalen DependencyProperties brauch man immer das normale Property wo im get und set nur SetValue und GetValue aufgerufen werden dürfen und nichts anderes. Der Grund liegt im XAML Parser, der geht nämlich genau von dieser Bennenung aus.

ok, gut zu wissen 🙂

Dein angepasstest Chinch Beispiel sieht aufm ersten Blick ganz okay aus. Wie lautet die Meldung denn genau wenn die Klasse statisch ist?

Also beim COde oben kommt folgende Exception:

Der Typeninitialisierer für "MVVMFrameWork.AttachedBehaviours.AutoScrollTextBoxBaseBehaviour" hat eine Ausnahme verursacht.

ist relativ nichtssagend, aber die InnerException besagt folgendes:

Der Typ "AutoScrollTextBoxBaseBehaviour" muss von "DependencyObject" abgeleitet sein.

Edit: @zero_x: Vielen Dank, genau da war mein Problem! 👍

Also vielen Dank nochmal an alle, jetzt bin ich wieder etwas schlauer geworden freu

20.12.2009 - 20:54 Uhr

so, ich habe jetzt mal das Beispiel von Cinch auf meine Bedürfnisse umgeändert, doch da gibts leider Probleme ... :

    /// <summary>
    /// This forces a <see cref="TextBoxBase"/> to scroll to end automatically when the <see cref="TextBoxBase.TextChanged"/>-Event is fired
    /// </summary>
    public static class AutoScrollTextBoxBaseBehaviour
    {
        #region Dependency Properties
        #region IsEnabledProperty
        /// <summary>
        /// Dependency-Property for enabling the autoscroll-feature
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof(bool), typeof(AutoScrollTextBoxBaseBehaviour), new UIPropertyMetadata(false, IsEnabledChangedCallback));
        /// <summary>
        /// Attached Property getter for the IsEnabled property.
        /// </summary>
        public static bool GetIsEnabled(DependencyObject source)
        {
            return (bool)source.GetValue(IsEnabledProperty);
        }
        /// <summary>
        /// Attached Property setter for the IsEnabled property.
        /// </summary>
        public static void SetIsEnabled(DependencyObject source, bool value)
        {
            source.SetValue(IsEnabledProperty, value);
        }
        /// <summary>
        /// The property changed handler for the IsEnabled property.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void IsEnabledChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            TextBoxBase tbb = sender as TextBoxBase;
            if (tbb == null) return;

            tbb.TextChanged -= OnTextChanged;

            bool b = ((e.NewValue != null && e.NewValue.GetType() == typeof(bool))) ? (bool)e.NewValue : false;
            if (b)
            {
                tbb.TextChanged += OnTextChanged;
            }

        }
        #endregion
        #endregion

        #region Private Methods
        /// <summary>
        /// If <see cref="TextBoxBase.TextChanged"/>-Event is fired, scroll to the end
        /// </summary>
        private static void OnTextChanged( object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            ((TextBoxBase)sender).ScrollToEnd();
        }
        #endregion
    }

der aufruf in XAMl sieht so bei mir aus:


            <RichTextBox VerticalScrollBarVisibility="Auto"
AttachedBehaviour:AutoScrollTextBoxBaseBehaviour.IsEnabled="True""/>

Also mein erstes Problem ist, dass er mir kein IntelliSense nach dem einfügen des ":" gibt. Wenn ich das ganze trotzdem so mache, gibts beim Starten des Programms eine Exception, welche besagt dass "AutoScrollTextBoxBaseBehaviour" von DependencyObject abgeleitet werden soll.

Also habe cih dass gemacht und das "static" bei der Klassendefinition rausgelöscht (sonst stresst wieder mal der Compiler). Dann kriege ich zwar Intellisense-Hilfe und kann das Programm auch starten, doch auto-scrollen tut er leider nicht.

Wo ist mein Fehler 🤔

Dann noch ne andere Frage: ist es irgendwo festgeschrieben, dass die Getter und Setter für die DPs so heißen müssen wie in meinem Code oben? denn ich frage mich wo diese aufgerufen werden und wenn ich den namen der Funktionen ändere, gibts die Property bei intellisense leider nicht mehr sondern heißt dann genauso wie meine get-funktion nur ohne Get *g. Genauso wenn ich ein DP mittels dem snippet "propdp" erstelle, ist diese auch nciht einsehbar durch Intellisense ... ?(

20.12.2009 - 19:48 Uhr

danke für das Beispiel.

Ich habe dazu jetzt ein Farge, und zwar muss ich dass immer so machen?

Zum Beispiel: Man möchte eine TextBox haben, bei welchem der gesamten Text, beim erhalten des Focus, selektiert werden soll. Da gibt's bestimmt noch mehr Beispiele (mir fällt grade nur ncihts ein *g).

Meiner Meinung nach ist dass ganze doch etwas nervig, immer eine Attached Behaviour zu basteln 😭.

20.12.2009 - 18:15 Uhr

Hallo,

ich hab grad ein kleines Problem mitm Expander, und zwar würde ich gerne den Pfeil, der angezeigt wird, umdrehen. Weil bei mir sit der Pfeil genau andersherum wie er sein sollte - er zeigt nach oben wenn er eig. nach unten zeigen müsste. (siehe angehäängtes Bild).

Ich könnte zwar: ExpandDirection="Up" schreiben - dann würde die Richtung des Pfeiles stimmen - doch ich würde es schon gerne so haben, dass der Pfeil + Überschrift oben bleibt.

MfG TripleX

20.12.2009 - 18:05 Uhr

Danke für die Antwort,

also mich hindert da z.B. dass ich im CodeBehind der View nichts schreiben will und ich ich im ViewModel kein Zugriff auf die RTB habe?

Mit AttachedBehaviour hatte ich bisher noch nichts am Hut, habe mir aber gerade folgendes angeschaut: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx aber irgendwie bringt mir das auch nicht weiter 🤔

könntest du bitte ein kleines Beispiel geben, wo so etwas ähnliches realisiert wurde?

MfG TripleX

20.12.2009 - 17:42 Uhr

Hallo Gemeinde,

ich hab gerade ein kleines Problem und zwar habe ich eine RichTextBox, welche automatisch befüllt wird. Jetzt würde ich es gerne so haben, dass wenn zuviel Text in der RTB angezeigt wird, die RTB automatisch nach unten scrollt.

"Normalerweise" wäre dass ja kein Problem, denn ich würde einfach ScrollToEnd() aufrufen, doch da ich dass Projekt mittels dem MVVM-Pattern durchziehen möchte, weiss ich gerade nicht wie ich dieses Problem lösen kann.

Ich habe versucht, die CaretPosition-Eigenschaft zu binden, doch dass ist nicht möglich da es kein DP ist.

MfG TripleX

20.12.2009 - 12:18 Uhr

das habe ich auch drin 😃

20.12.2009 - 07:28 Uhr

Hallo Gemeinde,

ich habe ein Projekt, indem 2 Buttons sind (Connect und Disconnect). Diese habe ich jeweils mit einem Command versehen und habe die CanExecute-Funktionen implementiert. Zum Beispiel soll beim Connect-Command der Button enabled sein, wenn eine bestimmte Propertie in einer anderen Klasse gesetzt ist, und der Disconnect button soll ebenfalls nur aktiviert werden, wenn der Benutzer bereits eingeloggt ist. Soweit funktioniert das ganz auch doch da gibt's nen kleinen Schönheitsfehler.

  1. Wenn ich ich auf connect klicke, wird der Button deaktiviert und ich werde nach meinen Logindaten gefragt, wenn diese falsch sind, sollte der Button wieder aktiviert werden. Dies passiert aber nur nur, wenn ich einmal kurz auf die Form klicke.

  2. Wenn die Logindaten richtig sind werde ich eingeloggt und danach sollte der Disconnect Button aktiviert werden, dies passiert aber auch erst nach einem Klick auf die Form.

Ich würde mir aber wünschen, wenn es auch ohne Click auf die Form passiert. Ich habe mir schon überlegt, ob es am NotifyPropertyChanged, welche bei den Properties (die abgefragt werden in der CanExecute) fehlen, liegt. Aber ich habe es zum Testen mal eingebaut, doch dass verhalten hat sich leider nicht verändert.

Weiss jemand woran das liegen könnte?

20.12.2009 - 06:53 Uhr

Hallo Gemeinde,

ich habe ein Projekt A, wo bei einer Funktion angegeben ist, dass sie nur im Debug-Build vorhanden sein soll. In einem anderen Projekt B habe ich Projekt A (Release Build) als Referenz hinzugefügt. Nun würde ich aber gerne wenn ich Projekt B im Debug-Mode starte, gerne auf diese Funktionen von Projekt A zugreifen. Doch diese gibt es natürlich nicht, da sie ja nur im Debug-Build vorhanden sind.

Natürlich könnte ich in Projekt B einfach den Debug-Build von Projekt A referenzieren, doch damit wäre ich nicht wirklich zufrieden, da ich am Ende dann die Referenzen wieder umtauschen muss (und sowas würde ich bestimmt vergessen *g)

Gibts für dieses Problem eine Lösung?

Konkret geht es bei Projekt A um Cinch (WPF-FrameWork), diese hat eine Funktion wie VerifyPropertyName welche automatisch aufgerufen wird, wenn ich NotifyPropertyChanged aufrufe, und diese überprüft ob der Name des Properties überhaupt vorhanden ist. Wenn sie nicht vorhanden ist, gibts ne Exception. Allerdings gibts diesen Service halt nur im DebugBuild (was ich auch gut finde, denn im Release _sollte _ja alles funktionieren und da braucht man die Funktion nicht.)

MfG TripleX

19.12.2009 - 01:40 Uhr

Vielen Dank euch allen, ihr habt mir wirklich sehr weitergeholfen!! 👍

MfG TripleX

18.12.2009 - 03:09 Uhr

Erstmal vielen Danke für die ganzen Antworten, ihr habt mir schonmal weitergeholfen.

Ein paar fragen habe ich doch noch:

In MVVM sollten DPs eigentlich nur in der View verwendet werden, da man ja von ausgeht das die View als einzige von WPF abhängig sein sollte. Mit DependencyProperties wäre das aber auch die ViewModelschicht und schon könnte man die schlecht woanders ohne WPF nutzen.

Hm ich dachte im codebehind der View sollte eigenlich gar nichts stehen (ausser konstruktor) - oder kann man DPs auch in XAML definieren?

Und was für einen Sinn hätten DPs in der View (oder anderen Schichten) - wie gesagt ich komme mit den DPs nicht wirklich auf einen grünen Zweig - ich verstehe einfach nicht wozu man diese benutzen kann bzw. sollte ... 🤔

Hier mal meine kurze Sichtweise über Properties, DPs und ChangeNotification, korrigiert mich bitte wenn ich irgendwo nen denkfehler habe:

Als ich vor ein paar Wochen mit WPF angefangen habe, habe ich mir eine ControlLibrary erstellt, wo ich viele kleinere UserControls hinzugefügt habe, wie "ImageButton". Dort habe ich z.B. einen MarginText, MarginImage, ImageSize, ... .
Dort wären glaube ich DPs angebracht.

Und für das "einfache" verbinden der View mit dem ViewModel sind DPs overkill und man ist mit INotifyPropertyChanged vollkommen zufrieden.

Dann gäbe es ja noch die "normalen Klassen", welche auch Properties haben, die brauchen meiner Meinung nach gar keine ChangeNotification und bleiben deswegen ganz normalen Eigenschaften.

Vielen Dank euch allen nochmal
MfG TripleX

17.12.2009 - 18:25 Uhr

Hallo,

ich wollte mal gerne wissen, wo der Unterschied zwischen INotifyPropertyChanged und DependencyProperties ist.

ich arbeite gerade an meinem eigenem kleinen MVVM-Framework und ich habe bisher alle "Proprerties" in meinem ViewModel als DependencyProperty implementiert. Doch demletzt habe ich gesehen, dass man das ganze auch mittels INotifyPropertyChanged programmieren könnte. Jetzt bin ich etwas confused und hoffe dass ich mit den DependencyProperties auf dem richtigen Weg bin, da diese mir einfach als "mächtiger" erscheinen. Doch den wirklichen Unterschied ist mir nicht wirklich klar.

Wann solte man DPs verwenden, und wann INotifyPropertyChanged?

MfG TripleX

17.12.2009 - 17:41 Uhr

also ich habe so etwas bereits programmiert (in Verbindung mit dem Command Pattern) - allerdings nicht in WPF sondern in Windows Forms. Ich habe dafür ein neues Control programmiert, welches abgeleitet ist von "ToolStripSplitButton", weiss grad net obs sowas auch für WPF gibt. Wenn gewünscht kann ich das Projekt hochladen und du kannst es dir mal anschauen.