WPF c# onStartUp() view model problem with multiple data inputs

david g 1 Reputation point
2022-02-05T18:14:06.04+00:00

Hey all I have the following OnStartup for my WPF desktop app that goes to a local directory and gathers all the images within that folder (box1 being the example code below):

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        // set the update interval
        var imageSource = new ImageSource(Path.Combine(@"C:\photos\boxes\", "box1"), TimeSpan.FromHours(1));
        var viewModel = new MainWindowViewModel(imageSource);

        var window = new MainWindow()
        {
            DataContext = viewModel
        };

        window.Closed += (s, a) => { viewModel.Dispose(); };
        window.Show();
    }
}

This works just fine for the component I have on the MainWindow.xaml that's label box1 but the other boxes 2-9 do not load their own images from their respective folders - they all show the same images as box1 has.

The structure of the directory is this:

 C:\
  |-photos\
     |boxes\
       |box1
       |box2
       |box3
       |box4
       |box5
       |box6
       |box7
       |box8
       |box9
       |box10

On the MainWindow I have this code that allows all those photos from each directory into its own element:

 <Window.Resources>
    <!-- List of supported animations -->
    <FluidKit:SlideTransition x:Key="SlideTransition" x:Shared="False"/>
    <FluidKit:CubeTransition x:Key="CubeTransition" Rotation="BottomToTop" x:Shared="False"/>
    <FluidKit:FlipTransition x:Key="FlipTransition" x:Shared="False"/>
    <local:ImageSourceConverter x:Key="ImageSourceConverter"/>
    <!-- Data template for animations -->
    <DataTemplate x:Key="ItemTemplate" x:Shared="False">
        <Image Source="{Binding Path, Converter={StaticResource ImageSourceConverter}}"
               Stretch="Fill"/>
    </DataTemplate>
</Window.Resources>
<Grid>
    <FluidKit:TransitionPresenter RestDuration="0:0:3" 
                                      IsLooped="True" 
                                    Transition="{StaticResource FlipTransition}" 
                                   ItemsSource="{Binding box1}" 
                                         Width="357" 
                                        Height="272" 
                           HorizontalAlignment="Left" 
                             VerticalAlignment="Top" 
                                  ItemTemplate="{StaticResource ItemTemplate}" 
                                        x:Name="box1" 
                                        Margin="0,0,0,454"/>
    <FluidKit:TransitionPresenter RestDuration="0:0:3" 
                                      IsLooped="True" 
                                    Transition="{StaticResource FlipTransition}" 
                                   ItemsSource="{Binding box2}" 
                                         Width="357" 
                                        Height="272" 
                           HorizontalAlignment="Left" 
                             VerticalAlignment="Top" 
                                  ItemTemplate="{StaticResource ItemTemplate}" 
                                        x:Name="box2" 
                                        Margin="357,0,0,0"/>
   ETC.......

Like I said above, this first element ItemsSource="{Binding box1}" loads the images as they should be from the box1 directory just fine but all the other 2-9 after that are loaded the same images as box1.

The mainWindowViewModel code looks like this:

public class MainWindowViewModel : IDisposable
{
    private readonly IDisposable _token1;
    private readonly IDisposable _token2;
    ...ETC

    public MainWindowViewModel(IImageSource imageSource)
    {
        // subscribe to update images at regular intervals
        _token1 = imageSource
            .GetImages().ObserveOn(DispatcherScheduler.Current)
            .Subscribe(i => UpdateImages(i, box1), ex => ShowError(ex));

        _token2 = imageSource
            .GetImages().ObserveOn(DispatcherScheduler.Current)
            .Subscribe(i => UpdateImages(i, box2), ex => ShowError(ex));
        ETC...
    }

    private void ShowError(Exception ex)
    {
        MessageBox.Show(ex.Message, "Photo Gallery", MessageBoxButton.OK, MessageBoxImage.Error);
    }

    /// <summary>
    /// Updates the animation's images.
    /// </summary>
    /// <param name="images">The images to update with</param>
    /// <param name="animation">The animation to update</param>
    private void UpdateImages(IEnumerable<string> images, ObservableCollection<ImageViewModel> animation)
    {
        animation.Clear();

        foreach (var i in images)
        {
            animation.Add(new ImageViewModel { Path = i });
        }
    }

    /// <summary>
    /// Gets or sets a collection of images used for the first animation. 
    /// </summary>
    public ObservableCollection<ImageViewModel> box1 { get; set; } =
        new ObservableCollection<ImageViewModel>();

    public ObservableCollection<ImageViewModel> box2 { get; set; } =
        new ObservableCollection<ImageViewModel>();

    ETC...

    public void Dispose()
    {
        _token1.Dispose();
        _token2.Dispose();
        ETC...
    }
}

The function that loops to get each file image within the directory is this:

public class ImageSource : IImageSource
{
    private readonly string _path;
    private readonly TimeSpan _interval;

    public ImageSource(string path, TimeSpan interval)
    {
        _path = path;
        _interval = interval;
    }

    public IObservable<IEnumerable<string>> GetImages()
    {
        if (!Directory.Exists(_path))
        {
            return Observable.Empty<IEnumerable<string>>();
        }

        return Observable.Create<IEnumerable<string>>(observer =>
        {
            return TaskPoolScheduler.Default.ScheduleAsync(async (ctrl, ct) =>
            {
                for (;;)
                {
                    if (ct.IsCancellationRequested)
                    {
                        break;
                    }

                    try
                    {
                        var images = Directory.GetFiles(_path, "*.jpg");

                        // Don’t do anything unless there are a minimum of 10 images.
                        if (images.Count() > 9)
                        {
                            observer.OnNext(images.PickRandom());
                        }
                    }
                    catch (Exception ex)
                    {
                        observer.OnError(ex);
                        throw;
                    }

                    await ctrl.Sleep(_interval).ConfigureAwait(false);
                 }
            });
        });
    }

The code currently goes to each of the ObservableCollection<ImageViewModel> box[X] { get; set; } and sets the path to each image within that folder. Box2-10 are the same files as box 1 of course.

How can I modify that onStartup() code to allow for it to consume each box folders' files and place them into the appropriate box # component instead of just using that box1 files?

Thanks!

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,671 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,237 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
762 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Leonardo Arrighi 1 Reputation point
    2022-02-07T22:01:01.463+00:00

    Hello @david g ,
    please take my answer with pliers (I'm only a passionate .NET developer, unfortunately not a professional).
    When you instance your service "ImageSource" in your App you pass the full path "C:\photos\boxes\box1" and while injecting the service in your VM you are instancing all tokens with the "box1" path so all of your "_token" instances are pointing at "C:\photos\boxes\box1".

    Instance the service "imageSource" with the path "C:\photos\boxes\" only, without combining the final path, try to pass the final path (\box1,\box2,\box3, etc) as a parameter to the function GetImages() and then combine the path.

    sorry for any misunderstanding :-)

    0 comments No comments