Elias pc->laptop

This commit is contained in:
unknown
2025-07-05 14:10:48 +02:00
parent 739e5ccfa6
commit 2abed3f2d8
237 changed files with 12168 additions and 1809 deletions

View File

@@ -123,9 +123,9 @@
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center"
VerticalAlignment="Bottom" Background="#222" KeyboardNavigation.DirectionalNavigation="None">
<TextBlock Name="title" Text="Titel" Foreground="White" FontSize="16" Margin="10"/>
<Button Content="⏮" Width="40" Margin="10" TabIndex="10" Focusable="True" Click="OnItemPriorButtonClick"/>
<Button Name="prior" Content="⏮" Width="40" Margin="10" TabIndex="10" Focusable="True" Click="OnItemPriorButtonClick"/>
<Button Content="▶" Width="40" Margin="10" TabIndex="11" Focusable="True" Click="OnItemPauseBtn_Click"/>
<Button Content="⏭" Width="40" Margin="10" TabIndex="12" Focusable="True" Click="OnItemNextButtonClick"/>
<Button Name="next" Content="⏭" Width="40" Margin="10" TabIndex="12" Focusable="True" Click="OnItemNextButtonClick"/>
<TextBlock Text="🔊" Foreground="White" VerticalAlignment="Center" Margin="10,0"/>
<Slider Name="vol" Width="100" Minimum="0" Maximum="100" Value="50" Margin="10"
TabIndex="13" Focusable="True" ValueChanged="RangeBase_OnValueChanged"/>

View File

@@ -59,7 +59,7 @@ public partial class MainWindow : Window
List<string> dirs = new List<string>();
private int option = 2;
int specificOption = 0;
string rootPath = "G:/";
string rootPath = "M:/Media";
FileScanner fileScanner;
@@ -123,7 +123,7 @@ public partial class MainWindow : Window
#endregion
#region BlutuethLogic
InitBluetoothServer();
//InitBluetoothServer();
#endregion
#region only exdend. no remuving of code
@@ -255,7 +255,8 @@ public partial class MainWindow : Window
VideoView.Visibility = Visibility.Collapsed;
}
private async void scanButton_Click(object sender, RoutedEventArgs e)
private async void scanButton_Click(object sender, RoutedEventArgs e)
{
try
{
@@ -299,68 +300,142 @@ public partial class MainWindow : Window
log.Log($"photoFiles count: {photoFiles.Count}");
log.Log($"series count: {series.Count}");
log.Log($"movies count: {movies.Count}");
progressScann = new ProgressBar("ImageGen",musicFiles.Count + videoFiles.Count + photoFiles.Count);
progressScann = new ProgressBar("ImageGen", musicFiles.Count + videoFiles.Count + photoFiles.Count);
progressScann.Show();
videoFiles = null;
log.Log("files sorted");
// Prepare JSON lists
List<locJason> muviesJS = new List<locJason>();
List<locJason> seriesJS = new List<locJason>();
List<locJason> photosJS = new List<locJason>();
List<locJason> mucicJS = new List<locJason>();
List<locJason> musicJS = new List<locJason>();
JasonToString jasonToString = new JasonToString();
// Use async ItemCreaterAsync
var movieItems = await ItemCreater(movies, "Muvie", false);
foreach (var VARIABLE in movieItems)
foreach (var item in movieItems)
{
Muvie.addItem(VARIABLE);
muviesJS.Add(new locJason
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
path = VARIABLE.getLink(),
imageData = jasonToString.BitmapToBase64String(BitmapConversions.BitmapImageToBitmap(VARIABLE.getImage())),
type = VARIABLE.getType()
});
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Muvie.addItem(item);
muviesJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing movie item {item.getLink()}: {exception.Message}");
log.Error($"Error processing movie item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Warning: Movie item {item.getLink()} has no image data.");
log.Log($"Warning: Movie item {item.getLink()} has no image data.");
}
}
var seriesItems = await ItemCreater(series, "Serie", false);
foreach (var VARIABLE in seriesItems)
foreach (var item in seriesItems)
{
Serie.addItem(VARIABLE);
seriesJS.Add(new locJason
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
path = VARIABLE.getLink(),
imageData = jasonToString.BitmapToBase64String(BitmapConversions.BitmapImageToBitmap(VARIABLE.getImage())),
type = VARIABLE.getType()
});
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Serie.addItem(item);
seriesJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing series item {item.getLink()}: {exception.Message}");
log.Error($"Error processing series item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Warning: Series item {item.getLink()} has no image data.");
log.Log($"Warning: Series item {item.getLink()} has no image data.");
}
}
var photoItems = await ItemCreater(photoFiles, "Photo", true);
foreach (var VARIABLE in photoItems)
foreach (var item in photoItems)
{
Photo.addItem(VARIABLE);
photosJS.Add(new locJason
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
path = VARIABLE.getLink(),
imageData = jasonToString.BitmapToBase64String(BitmapConversions.BitmapImageToBitmap(VARIABLE.getImage())),
type = VARIABLE.getType()
});
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Photo.addItem(item);
photosJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing photo item {item.getLink()}: {exception.Message}");
log.Error($"Error processing photo item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Warning: Photo item {item.getLink()} has no image data.");
log.Log($"Warning: Photo item {item.getLink()} has no image data.");
}
}
var musicItems = await ItemCreater(musicFiles, "Music", false, true);
foreach (var VARIABLE in musicItems)
var musicItems = await ItemCreater(musicFiles, "Music", false);
foreach (var item in musicItems)
{
Music.addItem(VARIABLE);
mucicJS.Add(new locJason
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
path = VARIABLE.getLink(),
imageData = jasonToString.BitmapToBase64String(BitmapConversions.BitmapImageToBitmap(VARIABLE.getImage())),
type = VARIABLE.getType()
});
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Music.addItem(item);
musicJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing music item {item.getLink()}: {exception.Message}");
log.Error($"Error processing music item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Info: Music item {item.getLink()} has no image data.");
log.Log($"Info: Music item {item.getLink()} has no image data.");
}
}
log.Log("scan finished");
@@ -371,13 +446,12 @@ public partial class MainWindow : Window
jason_Writer.SaveList("Muvies", muviesJS),
jason_Writer.SaveList("Series", seriesJS),
jason_Writer.SaveList("Photos", photosJS),
jason_Writer.SaveList("Music", mucicJS)
jason_Writer.SaveList("Music", musicJS)
};
await Task.WhenAll(tasks);
// Clear lists to free memory
mucicJS = null;
musicJS = null;
seriesJS = null;
photosJS = null;
@@ -387,7 +461,7 @@ public partial class MainWindow : Window
}
catch (Exception ex)
{
MessageBox.Show($"An error occurred during scanning:\n\n{ex}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
MessageBox.Show($"An error occurred during scanning:\n\n{ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
log.Error($"An error occurred during scanning:\n\n{ex}");
}
}
@@ -428,6 +502,7 @@ public partial class MainWindow : Window
ScrollContentCat.Visibility = Visibility.Collapsed;
ScrollContentHome.Visibility = Visibility.Visible;
MenueItems();
SetInitialFocusForCurrentView();
}
private void Musik_OnClick(object sender, RoutedEventArgs e)
{
@@ -439,6 +514,7 @@ public partial class MainWindow : Window
ScrollContentCat.Visibility = Visibility.Visible;
Name_of_Catagory1.Visibility = Visibility.Collapsed;
MenueItems(ref Music);
SetInitialFocusForCurrentView();
}
private void Photo_OnClick(object sender, RoutedEventArgs e)
{
@@ -450,6 +526,7 @@ public partial class MainWindow : Window
ScrollContentCat.Visibility = Visibility.Visible;
Name_of_Catagory1.Visibility = Visibility.Collapsed;
MenueItems(ref Photo);
SetInitialFocusForCurrentView();
}
private void Video_OnClick(object sender, RoutedEventArgs e)
{
@@ -460,6 +537,7 @@ public partial class MainWindow : Window
ScrollContentCat.Visibility = Visibility.Visible;
Name_of_Catagory1.Visibility = Visibility.Visible;
MenueItems(ref Muvie,ref Serie);
SetInitialFocusForCurrentView();
}
private void PlayListButton_OnClick(object sender, RoutedEventArgs e)
{
@@ -471,6 +549,8 @@ public partial class MainWindow : Window
Name_of_Catagory1.Visibility = Visibility.Collapsed;
PopulateAllCategoriesFromPlaylists(); // <-- This needs to set ScrollContentPlaylist visible
SetInitialFocusForCurrentView();
}
@@ -480,51 +560,18 @@ public partial class MainWindow : Window
#region itemCreation
async Task<List<Item>> ItemCreater(List<string> paths, string type, bool isFoto)
{
var semaphore = new SemaphoreSlim(Math.Max(1, Environment.ProcessorCount / 2));
var tasks = paths.Select(async path =>
{
await semaphore.WaitAsync();
try
{
BitmapImage frame200 = null;
if (!isFoto)
{
frame200 = await Task.Run(() => VideoFrameExtractor.GetFrame200(path));
}
else
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
frame200 = new BitmapImage(new Uri(path, UriKind.Absolute));
frame200.DecodePixelWidth = 150;
frame200.DecodePixelHeight = 100;
});
}
Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1));
return new Item(path, type, frame200, isFoto, OnItemPlayButtonClick);
}
finally
{
semaphore.Release();
}
}).ToList();
return (await Task.WhenAll(tasks)).ToList();
}
public async Task<List<Item>> ItemCreater(List<string> paths, string type, bool isFoto, bool isMusic = false)
public async Task<List<Item>> ItemCreater(List<string> paths, string type, bool isFoto)
{
var itemsBag = new ConcurrentBag<Item>();
var workQueue = new ConcurrentQueue<string>(paths);
int maxWorkers = Math.Max(1, Environment.ProcessorCount / 2);
var workers = new List<Task>();
// Determine if it's music based on 'type' string
bool isMusic = (type == "Music");
// Prepare default image for music if needed
BitmapImage defaultImage = null;
// Semaphore for concurrency control
var semaphore = new SemaphoreSlim(Math.Max(1, Environment.ProcessorCount / 2));
// ConcurrentBag for thread-safe collection of items
var itemsBag = new ConcurrentBag<Item>();
// Pre-load default music image once, if applicable
BitmapImage defaultMusicImage = null;
if (isMusic)
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
@@ -532,85 +579,147 @@ public partial class MainWindow : Window
if (System.IO.File.Exists(imagePath))
{
await Application.Current.Dispatcher.InvokeAsync(() =>
try
{
defaultImage = new BitmapImage(new Uri(imagePath));
defaultImage.DecodePixelWidth = 150;
defaultImage.DecodePixelHeight = 100;
defaultImage.Freeze();
});
await Application.Current.Dispatcher.InvokeAsync(() =>
{
defaultMusicImage = new BitmapImage(new Uri(imagePath));
defaultMusicImage.DecodePixelWidth = 150;
defaultMusicImage.DecodePixelHeight = 100;
defaultMusicImage.Freeze(); // Crucial for cross-thread access
});
}
catch (Exception ex)
{
// Log error if default image itself cannot be loaded
Console.WriteLine($"Error loading default music image {imagePath}: {ex.Message}");
// Consider adding to your specific logger here: log.Error(...)
defaultMusicImage = null;
}
}
else
{
MessageBox.Show($"Default image not found at: {imagePath}");
// Inform the user if the default image is missing
MessageBox.Show($"Default music image not found at: {imagePath}");
// Consider adding to your specific logger here: log.Error(...)
}
}
for (int i = 0; i < maxWorkers; i++)
// Create a list of tasks, one for each path
var tasks = paths.Select(async path =>
{
workers.Add(Task.Run(async () =>
{
while (workQueue.TryDequeue(out var filePath))
{
BitmapImage bitmapImage = null;
await semaphore.WaitAsync(); // Acquire a slot from the semaphore
BitmapImage bitmapImage = null; // To hold the image for the current item
Item newItem = null; // The Item object to be created
try
{
if (isMusic) // Logic for Music files: extract album art or use default
{
bitmapImage = defaultMusicImage; // Start with the default image
TagLib.File file = null; // TagLib.File object for disposing
try
{
if (isMusic)
file = TagLib.File.Create(path); // Create TagLib file instance
if (file.Tag.Pictures.Length > 0) // Check if album art exists
{
bitmapImage = defaultImage;
var pic = file.Tag.Pictures[0];
var ms = new System.IO.MemoryStream(pic.Data.Data); // Get image data as MemoryStream
try
await Application.Current.Dispatcher.InvokeAsync(() =>
{
var file = TagLib.File.Create(filePath);
if (file.Tag.Pictures.Length > 0)
{
var pic = file.Tag.Pictures[0];
var ms = new System.IO.MemoryStream(pic.Data.Data);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
var img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
ms.Position = 0;
img.StreamSource = ms;
img.EndInit();
img.Freeze();
img.DecodePixelWidth = 150;
img.DecodePixelHeight = 100;
bitmapImage = img;
});
ms.Dispose();
}
}
catch
{
// keep defaultImage on failure
}
var img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad; // Load full image data
ms.Position = 0; // Reset stream position
img.StreamSource = ms; // Set image source from stream
img.EndInit();
img.Freeze(); // Make immutable for performance
img.DecodePixelWidth = 150;
img.DecodePixelHeight = 100;
bitmapImage = img; // Overwrite default with extracted cover
});
ms.Dispose(); // Dispose MemoryStream
}
}
catch
catch (Exception tagLibEx)
{
// fallback: bitmapImage stays null or default
// Catch errors during TagLib processing (e.g., corrupted file, unsupported format)
Console.WriteLine($"Error extracting album art for {path}: {tagLibEx.Message}");
// Consider adding to your specific logger here: log.Error(...)
// bitmapImage remains defaultMusicImage or null if default also failed
}
// Create Item on UI thread because it creates UI controls internally
Item newItem = null;
await Application.Current.Dispatcher.InvokeAsync(() =>
finally
{
newItem = new Item(filePath, type, bitmapImage, isFoto, OnItemPlayButtonClick);
});
itemsBag.Add(newItem);
Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1));
file?.Dispose(); // Ensure TagLib.File is disposed to release file locks
}
}
else if (isFoto) // Logic for Photo files: direct image loading
{
try
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load full image data
bitmapImage.UriSource = new Uri(path, UriKind.Absolute); // Set URI source
bitmapImage.DecodePixelWidth = 150;
bitmapImage.DecodePixelHeight = 100;
bitmapImage.EndInit();
bitmapImage.Freeze(); // Make immutable
});
}
catch (Exception ex)
{
// Catch errors during photo image loading
Console.WriteLine($"Error loading photo image {path}: {ex.Message}");
// Consider adding to your specific logger here: log.Error(...)
bitmapImage = null; // Set image to null on failure
}
}
else // Logic for Video files: using VideoFrameExtractor
{
// VideoFrameExtractor.GetFrame200 should return null on failure (as previously discussed)
bitmapImage = await Task.Run(() => VideoFrameExtractor.GetFrame200(path));
}
}));
}
await Task.WhenAll(workers);
// Create the Item object on the UI thread
await Application.Current.Dispatcher.InvokeAsync(() =>
{
// newItem will have the extracted image, default image, or null if all failed
newItem = new Item(path, type, bitmapImage, isFoto, OnItemPlayButtonClick);
});
itemsBag.Add(newItem); // Add the created item to the thread-safe bag
Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1)); // Update progress on UI thread
}
catch (Exception ex)
{
// Catch any unexpected exceptions during the processing of a single item
Console.WriteLine($"Unexpected error processing item {path}: {ex.Message}");
// Consider adding to your specific logger here: log.Error(...)
// Create a fallback Item with a null image to ensure the task completes
await Application.Current.Dispatcher.InvokeAsync(() =>
{
newItem = new Item(path, type, null, isFoto, OnItemPlayButtonClick);
});
itemsBag.Add(newItem); // Add this fallback item
Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1)); // Still update progress
}
finally
{
semaphore.Release(); // Release the semaphore slot regardless of success or failure
}
return newItem; // Return the processed item (can have null image)
}).ToList(); // Convert the collection of tasks to a List<Task<Item>>
// Wait for all individual item processing tasks to complete
await Task.WhenAll(tasks);
// Convert the ConcurrentBag to a List and return
return itemsBag.ToList();
}
@@ -874,21 +983,134 @@ public partial class MainWindow : Window
MessageBox.Show($"📨 Message: {msg.Trim()}");
switch (msg.Trim())
{
//arrow up
case "\u2191": MessageBox.Show("case 1"); break;
//arrow down
case "\u2193": MessageBox.Show("case 2"); break;
//arrow left
//arrow right
//double left
//double right
//enter
//home
//vol +
//vol -
//mute
//time stamp
}
// Arrow Up
case "↑": // \u2191
var keyEventArgs = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Up) // simulate pressing the "A" key
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs);
break;
// Arrow Down
case "↓": // \u2193
var keyEventArgs2 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Down) // simulate pressing the "A" key
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs2);
break;
// Arrow Left
case "←": // \u2190
var keyEventArgs3 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Left) // simulate pressing the "A" key
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs3);
break;
// Arrow Right
case "→": // \u2192
var keyEventArgs4 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Right) // simulate pressing the "A" key
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs4);
break;
// Double Left
case "⇚": // \u21DA
var clickEventArgs1 = new RoutedEventArgs(Button.ClickEvent);
prior.RaiseEvent(clickEventArgs1);
break;
// Double Right
case "⇛": // \u21DB
var clickEventArgs2 = new RoutedEventArgs(Button.ClickEvent);
next.RaiseEvent(clickEventArgs2);
break;
// Center button (Dot)
case "●": // \u25CF
if (_mediaPlayer.IsPlaying)
{
var keyEventArgs5 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Space) // simulate pressing the "A" key
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs5);
}
else
{
var keyEventArgs5 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Enter) // simulate pressing the "A" key
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs5);
}
break;
case "Vol -":
if(_mediaPlayer.Volume>10)
_mediaPlayer.Volume = _mediaPlayer.Volume - 10;
else
_mediaPlayer.Volume = 10;
vol.Value = _mediaPlayer.Volume;
break;
case "Vol +":
if(_mediaPlayer.Volume<90)
_mediaPlayer.Volume = _mediaPlayer.Volume + 10;
else
_mediaPlayer.Volume = 100;
vol.Value = _mediaPlayer.Volume;
break;
case string s when s.StartsWith("Time:"):
string valuePart = s.Substring("Time:".Length);
if (double.TryParse(valuePart, out double timeValue))
{
_mediaPlayer.Time= Convert.ToInt32(valuePart);;
}
else
{
MessageBox.Show("Invalid time format.");
}
break;
default:
MessageBox.Show($"Unrecognized input: '{msg}'");
break;
}
});
@@ -1317,7 +1539,8 @@ public partial class MainWindow : Window
{
itemProgress.Value = timeStap/1000;
itemProgressVisual.Value = itemProgress.Value;
Server1.SendMessage("Progress="+itemProgress.Value);
if(Server1!=null)
Server1.SendMessage("Progress="+itemProgress.Value);
}
private void ProgressBarValueChange()
{
@@ -1413,13 +1636,14 @@ public partial class MainWindow : Window
#endregion
#region arrow nav
#region arrow nav
private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.FocusedElement is not DependencyObject focusedElement)
return;
var wrapPanel = FindAncestor<WrapPanel>(focusedElement);
var stackPanel = FindAncestor<StackPanel>(focusedElement);
if (wrapPanel != null && wrapPanel.Children.Count > 0)
{
@@ -1450,81 +1674,121 @@ private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
case Key.Up:
e.Handled = true;
if (focusedIndex < columns)
{
if (stackPanel != null)
{
int currentWrapPanelIndex = stackPanel.Children.IndexOf(wrapPanel);
if (currentWrapPanelIndex > 0)
{
for (int i = currentWrapPanelIndex - 1; i >= 0; i--)
{
if (stackPanel.Children[i] is WrapPanel prevWrapPanel && prevWrapPanel.Children.Count > 0)
{
int targetIndexInPrev = Math.Min(prevWrapPanel.Children.Count - 1, focusedIndex + ((prevWrapPanel.Children.Count / columns) * columns));
MoveFocusInWrapPanel(prevWrapPanel, targetIndexInPrev);
return;
}
}
}
}
}
MoveFocusInWrapPanel(wrapPanel, focusedIndex - columns);
break;
case Key.Down:
e.Handled = true;
if (focusedIndex >= wrapPanel.Children.Count - columns)
{
if (stackPanel != null)
{
int currentWrapPanelIndex = stackPanel.Children.IndexOf(wrapPanel);
if (currentWrapPanelIndex != -1 && currentWrapPanelIndex < stackPanel.Children.Count - 1)
{
for (int i = currentWrapPanelIndex + 1; i < stackPanel.Children.Count; i++)
{
if (stackPanel.Children[i] is WrapPanel nextWrapPanel && nextWrapPanel.Children.Count > 0)
{
int targetIndexInNext = Math.Min(columns - 1, nextWrapPanel.Children.Count - 1);
MoveFocusInWrapPanel(nextWrapPanel, targetIndexInNext);
return;
}
}
}
}
}
MoveFocusInWrapPanel(wrapPanel, focusedIndex + columns);
break;
}
}
else if (FirstCat.IsFocused && e.Key == Key.Right)
{
// Find the first visible WrapPanel child in your current visible ScrollViewer
e.Handled = true; // Always handle the key if we are trying to move from FirstCat
var firstFocusable = FindFirstFocusableInVisibleWrapPanel();
if (firstFocusable != null)
{
firstFocusable.Focus();
e.Handled = true;
}
else
{
// Fallback: Wenn kein spezifisches fokussierbares Element gefunden wurde, versuchen Sie, den ScrollViewer selbst zu fokussieren
if (ScrollContentHome.Visibility == Visibility.Visible) ScrollContentHome.Focus();
else if (ScrollContentCat.Visibility == Visibility.Visible) ScrollContentCat.Focus();
else if (ScrollContentPlaylist.Visibility == Visibility.Visible) ScrollContentPlaylist.Focus();
}
}
}
// Finds the first focusable control in the first visible WrapPanel child of your main ScrollViewer content
private Control FindFirstFocusableInVisibleWrapPanel()
private Control FindFirstFocusableInVisibleWrapPanel()
{
ScrollViewer currentScrollViewer = null;
if (ScrollContentHome.Visibility == Visibility.Visible)
{
ScrollViewer currentScrollViewer = null;
if (ScrollContentHome.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentHome;
}
else if (ScrollContentCat.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentCat;
}
else if (ScrollContentPlaylist.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentPlaylist;
}
else
{
Console.WriteLine("No visible ScrollViewer found");
return null;
}
Panel mainPanel = currentScrollViewer.Content as Panel;
if (mainPanel != null)
{
for (int i = 0; i < mainPanel.Children.Count; i++)
{
var child = mainPanel.Children[i];
WrapPanel wp = child as WrapPanel;
if (wp != null && wp.Visibility == Visibility.Visible && wp.Children.Count > 0)
{
Control firstFocusable = FindFirstFocusableChild(wp);
if (firstFocusable != null)
{
return firstFocusable;
}
}
}
}
else
{
Console.WriteLine("ScrollViewer content is not a Panel");
}
Control fallback = FindFirstFocusableChild(currentScrollViewer.Content as DependencyObject);
if (fallback != null)
return fallback;
currentScrollViewer = ScrollContentHome;
}
else if (ScrollContentCat.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentCat;
}
else if (ScrollContentPlaylist.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentPlaylist;
}
else
{
Console.WriteLine("No visible ScrollViewer found");
return null;
}
Panel mainPanel = currentScrollViewer.Content as Panel;
if (mainPanel != null)
{
for (int i = 0; i < mainPanel.Children.Count; i++)
{
var child = mainPanel.Children[i];
WrapPanel wp = child as WrapPanel;
if (wp != null && wp.Visibility == Visibility.Visible && wp.Children.Count > 0)
{
Control firstFocusable = FindFirstFocusableChild(wp);
if (firstFocusable != null)
{
return firstFocusable;
}
}
}
}
else
{
Console.WriteLine("ScrollViewer content is not a Panel");
}
// ... rest of your helpers remain unchanged ...
Control fallback = FindFirstFocusableChild(currentScrollViewer.Content as DependencyObject);
if (fallback != null)
return fallback;
return null;
}
private int CalculateColumns(WrapPanel panel)
{
@@ -1534,7 +1798,7 @@ private int CalculateColumns(WrapPanel panel)
if (firstChild == null) return 1;
double itemWidth = firstChild.ActualWidth + firstChild.Margin.Left + firstChild.Margin.Right;
if (itemWidth <= 1) itemWidth = 150; // fallback
if (itemWidth <= 1) itemWidth = 150;
double panelWidth = panel.ActualWidth;
int columns = Math.Max(1, (int)(panelWidth / itemWidth));
@@ -1545,14 +1809,22 @@ private bool IsLeftmostInRow(int index, int columns) => (index % columns) == 0;
private void MoveFocusInWrapPanel(WrapPanel panel, int index)
{
if (index < 0 || index >= panel.Children.Count) return;
if (index < 0) index = 0;
if (index >= panel.Children.Count) index = panel.Children.Count - 1;
if (panel.Children[index] is Control ctrl && ctrl.Focusable)
{
ctrl.Focus();
ctrl.BringIntoView();
}
else if (panel.Children[index] is Panel container)
{
var focusable = FindFirstFocusableChild(container);
focusable?.Focus();
if (focusable != null)
{
focusable.Focus();
focusable.BringIntoView();
}
}
}
@@ -1606,6 +1878,26 @@ private static T FindAncestor<T>(DependencyObject child) where T : DependencyObj
}
return null;
}
public void SetInitialFocusForCurrentView()
{
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, new Action(() =>
{
Control firstFocusable = FindFirstFocusableInVisibleWrapPanel();
if (firstFocusable != null)
{
firstFocusable.Focus();
}
else
{
if (ScrollContentHome.Visibility == Visibility.Visible) ScrollContentHome.Focus();
else if (ScrollContentCat.Visibility == Visibility.Visible) ScrollContentCat.Focus();
else if (ScrollContentPlaylist.Visibility == Visibility.Visible) ScrollContentPlaylist.Focus();
}
}));
}
#endregion
}