Zitat von Th69
Außerdem ist der Code bzgl. des Cachen der Icons anhand des
MainWindowHandle
nicht durchdacht, denn z.B. nach einem Neustart ändern sich diese ja und es würden die falschen Icons angezeigt.
Das hatte ich bedacht, dass sich der MainWindowHandle ändert und den Cache-Ordner immer geleert.
Zitat von jogibear9988
- Die Bilder nicht ins Dateisystem legen sondern nur im Speicher halten. bspw in der Runnings App Klasse Statt dem Pfad, Direkt das Image bzw die Bildaten halten.
Mein Code, um die Icons im Speicher zu halten, sieht jetzt wie folgt aus:
private void TaskbarTimer_Elapsed(object sender, ElapsedEventArgs e)
{
List<RunningProgram> runningPrograms = new List<RunningProgram>();
Parallel.ForEach(Process.GetProcesses(), p =>
{
try
{
if (!String.IsNullOrEmpty(p.MainWindowTitle))
{
string processPath = p.MainModule.FileName;
MemoryStream memoryStream = new MemoryStream();
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(processPath);
icon.ToBitmap().Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
PngBitmapDecoder pngBitmapDecoder = new PngBitmapDecoder(memoryStream,
BitmapCreateOptions.None,
BitmapCacheOption.Default);
runningPrograms.Add(new RunningProgram()
{
Name = p.MainWindowTitle,
Path = processPath,
BitmapSource = pngBitmapDecoder.Frames[0],
Handle = p.MainWindowHandle
}
);
}
}
catch
{
}
}
);
Dispatcher.Invoke(() =>
{
Taskbar.Children.Clear();
foreach (RunningProgram runningProgram in runningPrograms.OrderBy(x => x.Name))
{
Button button = new Button()
{
Content = new System.Windows.Controls.Image
{
Stretch = Stretch.Fill,
Source = runningProgram.BitmapSource
},
Tag = runningProgram.Handle,
ToolTip = runningProgram.Name
};
button.Click += Button_Click;
// button.Width = 50;
// button.Height = 50;
Taskbar.Children.Add(button);
}
}
);
taskbarTimer.Start();
}
Zitat von jogibear9988
- Die Running apps liste nur mit den neuen Prozessen füllen und die alten entfernen und nicht immer neu bauen
Da fehlt mir gerade die Idee, wie ich das umsetzen soll. Muss ich dann die generische ListerunningPrograms
global defineren? Findet die Überprüfung der laufenden Prozesse auch im Elapsed-Event statt?
Zitat von jogibear9988
- Die UI nur so aktualiseren das neue Prozesse dazukommen und alte entfernt werden, nicht immer alles neu Zeichnen
- Die Liste in XAML mit ItemsTemplate und ItemsSource füllen und nicht die Objekte im Code anlegen
Das ist glaube ich, ist noch zu hoch für mich.
Vielen Dank für Eure Hilfe.
Vielen Dank für Deinen Code. Das hätte ich so (noch) nicht hinbekommen...
Ich finde den Code nicht gut...
Warum das bild immer abspeichern? Warum die List eimmer aktualisieren, auch wenn sich z.B. nichts geändert hat, ...
Das geht sicher besser.
Wie könnte ich den Code hier optmieren?
@Abt.
Vielen Dank für Deine Mühe und die weiterführenden Links.
Da kommt ja noch ein großer Brocken Arbeit auf mich zu ....
Zitat von Abt
Du kannst mit dem Debugger schauen, was langsam ist. Wir müssten nun mit der Glaskugel raten.
[Artikel] Debugger: Wie verwende ich den von Visual Studio?
Aus dem Artikel kann ich nicht entnehmen, wie ich ich mit dem Debugger feststellen kann, wie lange Programmteile für die Ausführung benötigt. Die Programmfenster werden erst sehr verzögert wiederhergestellt. (zwei bis drei Sekunden nach dem auf die jeweilige Schaltfläche geklickt wurde)
Danke für das Feedback. Da ergeben sich doch Fragen als C#-Neuling.
- WPF ist so konzipiert, dass Du MVVM anwendest, was hier nicht der Fall ist
Ganz oberflächlich, weiß ich gerade mal, was die Abkürzung MVVM bedeutet. Wie sieht da konkret für mein Progrämmchen aus?
- Der korrekte Start für einen Hintergrundtask ist eben ein Task, kein DispatcherTimer
Wie ermittelte ich dann regelmäßig die Prozesse? Wie es über ein Hintergrundtask funktionieren soll, verstehe ich nicht.
- Das Laden sollte asynchron sein, sonst hängt dein UI (dafür kann man auch den Window_Loaded Event abonnieren und via async/await arbeiten)
Was soll hier asynchron abgearbeitet werden?
Danke dass Ihr Euch die Zeit für die Beantwortung nehmt.
@Communtiy.
In meiner WPF-Anwendung werden die geöffneten Programme als Schaltflächen in einem Stackpanel abgelegt. Bei einem Klick auf die Schaltfläche soll das jeweilige Programmfenster wieder angezeigt und aktiviert werden. (Ähnlich wie wenn auf die Programmsymbol in der Taskleiste geklickt wird) Es funktioniert leider nur sehr mässig. Es dauert eine Weile bis das jeweilige Programmfenster wiederhergestellt wird. Der Benutzer hat keinen Zugriff weder auf dem Desktop noch auf die Taskleiste. Daher diese Lösung.
Für jede Art für Unterstützung wäre ich sehr dankbar.
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
public MainWindow()
{
InitializeComponent();
DispatcherTimer dt = new DispatcherTimer() {
Interval = TimeSpan.FromMilliseconds(500)
};
dt.Tick += new EventHandler(dispatcherTimer_Tick);
dt.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
OpenProgramms.Children.Clear();
foreach (Process p in Process.GetProcesses())
{
string processPath = String.Empty;
try
{
processPath = p.MainModule.FileName;
}
catch ()
{
continue;
}
if (String.IsNullOrEmpty(p.MainWindowTitle))
continue;
string iconPath = $"\\cache\\{p.MainWindowHandle}";
if (!File.Exists(iconPath))
{
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(processPath);
icon.ToBitmap().Save(iconPath);
icon.Dispose();
}
Button button = new Button()
{
Content = new System.Windows.Controls.Image
{
Stretch = Stretch.Fill,
Source = new BitmapImage(new Uri(iconPath))
},
Tag = p.MainWindowHandle,
ToolTip = p.ProcessName
};
button.Click += Button_Click;
OpenPrograms.Children.Add(button);
}
}
private void Button_Click(object sender, EventArgs e)
{
conse int SW_RESTORE = 9:
IntPtr handle = (IntPtr)((Button)sender).Tag;
bool retValue = ShowWindowAsync(handle, SW_RESTORE);
}
}