In WPF könnte dies so aussehen:
Pakete CommunityToolkit.Mvvm installieren für das RelayCommand.
Microsoft.Xaml.Behaviors.Wpf installieren für die Eventverarbeitung
Ein MainWindow erstellen, ViewModel (vm) und Behaviors (behav) definieren.
Datenbindung für das Window loaded event und den Text für das Label erstellen.
Ich nutze doch nur eine Abkürzung um die exe aus zu führen und trotzdem ist ein Workingdir notwendig? Was passiert da im Hintergrund?
Die Antwort hast Du Dir doch im Eingangspost schon selber gegeben! Deine Exe findet doch auch die Dateien nicht selber, wenn sie in einem anderen Ordner als die CMD-Dateien liegt.
Vereinfacht gesagt, ist der Arbeitsordner immer der Ordner, in dem das Programm gestartet wird, wenn nichts anderes angegeben wird. In Deinem Fall mit der Verknüpfung wird das Programm halt dann wahrscheinlich vom Desktop getsartet und der Arbeitsordner ist eben der Desktop.
public class MainWindowViewModel
{
public ObservableCollection<Auto> AutoListe { get; set; }
public MainWindowViewModel()
{
AutoListe = new ObservableCollection<Auto>()
{
new Auto() { Marke = "Audi", Modell = "R8", PS = 610, Logo = "Images/logo_audi.jpg" },
new Auto() { Marke = "Bentley", Modell = "CS", PS = 630, Logo = "Images/logo_bentley.jpg" },
new Auto() { Marke = "Lambo", Modell = "LP 700", PS = 700, Logo = "Images/logo_lambo.jpg" },
new Auto() { Marke = "SEAT", Modell = "DSG", PS = 300, Logo = "Images/logo_seat.jpg" },
new Auto() { Marke = "Skoda", Modell = "RS", PS = 230, Logo = "Images/logo_skoda.jpg" },
new Auto() { Marke = "VW", Modell = "TDI 8", PS = 240, Logo = "Images/logo_vw.jpg" }
};
}
}
Erstelle eine Instanz des Viemodels in der View und definiere die Listbox. Das Datatemplate muss nach den Bedürfnissen angepasst werden.
Ich bekomme den Fehler: "The JSON value could not be converted to System.String. Path: $.success | LineNumber: 0 | BytePositionInLine: 15."
Bei diesem Code:
public class Mitarbeiter
{
public string success { get; set; }
public Data data { get; set; }
}
Ändere die Mitarbeiterklasse wie folgt:
public class Mitarbeiter
{
public bool success { get; set; }
public Data data { get; set; }
}
@Caveman das sieht nach .NET 3.5 Code oder früher aus.
Seit .NET 4 kann man einfach CopyTo verwenden
Ich habe in dem Fall einfach nur stur gegoogelt und die erstbesten Beispiele von StackOverflow kopiert.
Dass mit CopyTo habe ich gelesen, wollte es aber nicht posten, weil es dann keine 5 Zeilen Code geworden wären
Deswegen hatte ich auch geschrieben, dass ich das selber nicht probiert hatte.
using (MemoryStream ms = new MemoryStream())
using (FileStream file = new FileStream("file.bin", FileMode.Open, FileAccess.Read)) {
byte[] bytes = new byte[file.Length];
file.Read(bytes, 0, (int)file.Length);
ms.Write(bytes, 0, (int)file.Length);
}
Im View Model eine Eigenschaft vom Typ byte[] anlegen
private byte[] userImage;
public byte[] UserImage
{
get { return userImage; }
set
{
if (value != userImage)
{
userImage = value;
OnPropertyChanged("UserImage");
}
}
}
Und im Xaml darauf binden
<Image Source="{Binding UserImage}"/>
Alles ungetestet! Das Byte-Array muss noch mit der Eigenschaft UserImage verbunden werden.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Forum124378II
{
public class MainWindowViewModel : ViewModelBase
{
private Navigator navigator;
private ViewModelFactory viewModelFactory;
private ViewModelTypes selectedViewType;
private string errorMessage;
public ViewModelBase CurrentViewModel
{
get { return navigator.CurrentViewModel; }
}
public string ErrorMessage
{
get { return errorMessage; }
set
{
if (errorMessage != value)
{
errorMessage = value;
OnPropertyChanged();
}
}
}
public IEnumerable<ViewModelTypes> ViewTypes
{
get
{
return Enum.GetValues(typeof(ViewModelTypes)).Cast<ViewModelTypes>();
}
}
public ViewModelTypes SelectedViewType
{
get { return selectedViewType; }
set
{
if (selectedViewType != value)
{
selectedViewType = value;
OnPropertyChanged();
UpdateView();
}
}
}
public MainWindowViewModel()
{
navigator = new Navigator();
viewModelFactory = new ViewModelFactory();
navigator.StateChanged += ViewModelChanged;
SelectedViewType = ViewModelTypes.Customers;
navigator.CurrentViewModel = viewModelFactory.Get(SelectedViewType);
}
private void UpdateView()
{
navigator.CurrentViewModel = viewModelFactory.Get(SelectedViewType);
}
private void ViewModelChanged()
{
OnPropertyChanged(nameof(CurrentViewModel));
if (CurrentViewModel is IMessage viewModelWithMessage)
{
ErrorMessage = viewModelWithMessage.Message;
}
}
}
}
Navigator:
using System;
namespace Forum124378II
{
public class Navigator
{
private ViewModelBase currentViewModel;
public ViewModelBase CurrentViewModel
{
get
{
return currentViewModel;
}
set
{
currentViewModel = value;
StateChanged?.Invoke();
}
}
public event Action StateChanged;
}
}
Wenn der Navigator ein neues ViewModel erhält, dann wird ein Event gefeuert.
Das MainViewModel hat dieses Event aboniert und reagiert darauf mit der Methode ViewModelChanged.
In der Methode wird die Eigenschaft CurrentViewModel aktualisiert, was einen Austausch des DataTemplates in der View zur Folge hat.
Weiterhin wird geprüft, ob das ViewModel die Schnittstelle IMessage implementiert.
Falls dem so ist, dann wird der Wert aus der Eigenschaft Message im CurrentViewModel in die Eigenschaft ErrorMessage geschrieben.
ganz kann ich dem Code im Eingangspost nicht folgen.
Deshalb eine Eigenkreation, die vielleicht das gewünschte berwerkstelligt.
Wie von Th69 schon angemerkt, solltest Du eine Basisklasse für Deine ViewModels haben:
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged_Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Von dieser Basisklasse leiten die folgenden zwei Klassen ab:
public class CustomerViewModel : ViewModelBase, IViewModelWithMessage
{
public string Message { get; private set; }
public CustomerViewModel(int i)
{
Message = $"Customer {i} is greeting you!";
}
}
public class NonCustomerViewModel : ViewModelBase
{
public NonCustomerViewModel()
{
}
}
Die CustomerViewModel Klasse implementiert auch das Interface für das Message-Property
public interface IViewModelWithMessage
{
string Message { get; }
}
Erzeugt werden die konkreten Klassen im MainWindowViewModel
public class MainWindowViewModel : ViewModelBase
{
public int Index { get; set; }
public List<ViewModelBase> ViewModels { get; set; }
private string newMessage;
public string NewMessage
{
get { return newMessage; }
set
{
if (newMessage != value)
{
newMessage = value;
OnPropertyChanged();
}
}
}
public ICommand MoveToNextViewModelCmd { get; }
public MainWindowViewModel()
{
Index = 0;
ViewModels = new List<ViewModelBase>();
for (int i = 0; i < 2; i++)
{
ViewModels.Add(new CustomerViewModel(i));
}
for (int i = 0; i < 3; i++)
{
ViewModels.Add(new NonCustomerViewModel());
}
for (int i = 5; i < 10; i++)
{
ViewModels.Add(new CustomerViewModel(i));
}
MoveToNextViewModelCmd = new MoveToNextViewModelCommand(this);
}
}
Beim Drücken auf den Button in der Mainview wird in der Kommando-Klasse der Text aus der Customer-Klasse in das MainWindowViewModel übertragen.
internal class MoveToNextViewModelCommand : ICommand
{
public event EventHandler? CanExecuteChanged;
private ViewModelBase viewModel;
public MoveToNextViewModelCommand(ViewModelBase anyViewModel)
{
viewModel = anyViewModel;
}
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
if (viewModel is MainWindowViewModel mainViewModel)
{
if (mainViewModel.Index == mainViewModel.ViewModels.Count)
{
mainViewModel.Index = 0;
}
if (mainViewModel.ViewModels[mainViewModel.Index] is IViewModelWithMessage viewModelWithMessage)
{
mainViewModel.NewMessage = viewModelWithMessage.Message;
}
else
{
mainViewModel.NewMessage = $"ViewModel at Index { mainViewModel.Index } is of type { mainViewModel.ViewModels[mainViewModel.Index] } and has therefore no Message-Property";
}
mainViewModel.Index++;
}
}
}
Ich persönlich habe noch nie eine "Generic.xaml" genutzt
Also wenn es für diesen speziellen Namen irgendwelche Sonderregelungen gibt, weiß ich nichts davon.
Diese zwei Sätze haben mir die Augen geöffnet, auch wenn wir da aneinander vorbei reden. Ich bin so ein Dödel. Hat mich jetzt keine 30 Minuten gekostet, das Projekt ans Laufen zu bekommen! Gestern ist mir das nach 10 Stunden Google-Suche und etlichen Gläsern Bowle nicht eingefallen.
Lösung: Der richtige Projekttyp ist wichtig. Statt "WPF-Klassenbibliothek" hätte ich "Bibliothek benutzerdefinierter WPF-Steuerelemente" auswählen müssen. Dann wird nämlich auch der Themes-Ordner mit Generic.xaml angelegt und verwendet!
Hallo,
ich habe begonnen, meine Controls unter Visual Studio 2022 aud .Net6 umzustellen. Dafür verwende ich ein eigenes Projekt (nur der Übung halber!).
Wie ich nun feststellen muss, wird der zum Control definierte Style aus der Generic.xaml nicht zugewiesen, so dass das komplette Control nicht sichtbar wird.
Google sagt, dass das früher schon immer mal ein Problem war und dass das an einem doppelten Generic.xaml liegen könnte. Die vorgeschlagenen Lösungen habe ich erfolglos durchprobiert. Wenn ich den kompletten Style im instantierten Control zuweise funktioniert alles.
Die Generic.xaml besteht nur aus einem ResourceDictionary, wo auf die xaml-Dateien für die einzelnen Controls verwiesen wird.
Ich vermute mal, dass die Bindungen dadurch zustande kommen, weil Du den DataContext vom DataTemplate direkt auf das ToolViewModel bindest. Wie Papst schon angemerkt hat, fehlt das Tools-Property im ViewModel.
In Deinem ViewModel musst Du ein Property Tools vom Typ List<ToolViewModel> definieren. Du brauchst in dem Fall keine ObservableCollection<ToolViewModel>, da sich die Anzahl der Tools nicht ändert.
Das DataTemplate ToolChangeView änderst Du wie folgt.
public class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<ToolViewModel> Tools { get; set; }
public MainWindowViewModel()
{
Tools = new ObservableCollection<ToolViewModel>
{
new ToolViewModel()
};
}
}
Das zweite Beispiel mit "with leading white space" hat dann immerhin ein Ergebnis gegeben, aber nicht, das, was ich mir erhofft habe. Umwandeln klappte natürlich damit erst recht nicht.
22.06.2021 00:00:00 +00:00
string stringDate = managementObject["InstalledOn"].ToString();
string format = "M/dd/yyyy"; // M/d/yyyy geht auch
CultureInfo provider = CultureInfo.InvariantCulture;
DateTimeOffset result = new DateTimeOffset();
result = DateTimeOffset.ParseExact(stringDate, format, provider, DateTimeStyles.AssumeUniversal);
systemLastWindowsUpdate = result.ToString();
In der Regel wird alles im XAML definiert. Im MVVM Pattern wird normalerweise nichts, aber auch gar nichts im Code behind programmiert.
Und ich fürchte, du hast ein Verständnisproblem mit den View Models. Willst Du für jedes Steuerelement ein eigenes ViewModel machen? Also auch ein CheckBoxViewModel, RadioButtonViewModel, TextBlockViewModel, DataGridViewModel, ...?
Was müssen Deine ViewModels außergewöhnliches können - außer Text anzeigen - dass man dies so wie von Dir gewünscht, definieren müßte?
Ändert sich die Ansicht?
es soll nach Möglichkeit eine beliebige Anzahl an Spalten hinzugefügt werden können.
Meine bisherige ItemsSource ist eine ObservableCollection von ViewModels, die eine Datenzeile repräsentieren - also ganz klassisch!
Die DataTable hatte ich gleich zu Beginn in betracht gezogen, dann aber ziemlich schnell verworfen, weil ich mehrfach gelesen habe, dass man diese nicht mehr verwenden sollte.
Werde mich aber am kommenden WE dann doch nochmals näher damit auseinandersetzen.
ich habe ein Datagrid, das einige fest definierte Spalten hat. Nun möchte ich gerne aus einer Exceltabelle willkürliche Spalten zur Laufzeit hinzuladen.
Das hinzuladen der Spalten an sich ist (im Moment) nicht das Problem. Seit ein paar Tagen mache ich mir aber Gedanken darüber, wie ich die Excelspalten mit meinem ViewModel verbinde, komme aber auf keine Lösung. Wie muss ich das angehen?
ich habe da mal eine Frage. Verwendest Du die Prism library?
public class MainVM : BindableBase
deutet jedenfalls darauf hin. Es gäbe nämlich in den Beispielprogrammen zu Prism mehrere Beispiele zur Navigation. Vielleicht helfen Dir diese weiter! Ich stand vor einigen Wochen vor dem gleichen Problem und mir haben diese Beispiele schon enorm geholfen.
in Deinem XAML sind alle Buttons ausser der 0 und 1 tot, da kein Click Event definiert ist.
Die beiden Buttons 0 und 1 definieren das gleiche Event, aber die zugehörige Cmd_1_Click Methode hast Du nicht gepostet.
Stattdessen aber Cmd_0_Click, die aber keinem Button zugeordnet ist.
In Deiner Cmd_0_Click Methode wird nicht ersichtlich, welcher Button gedrückt ist und einfach immer nur stur eine weitere 0 im Label angefügt.
Deswegen verwende ich jetzt mal die Methode als Cmd_1_Click für eine mögliche Lösung: