0

I want to change or "scroll through" different background images for the main window based on a button click. The number of different backgrounds will be dynamic and will be based on something like the number of images in a specific folder. So each time the program loads there could be a different number of backgrounds to scroll through.

I also want to be able to go back to the previous background image, so the whole thing kind of acts like a carousel. Example: the program loads and A.jpg is loaded as background image. I click the "Right" button and A.jpg slides off to the left, and then B.jpg slides in from the right to become the new background image. I click "Right" again and C.jpg slides in from the right. I then click "Left" and B.jpg slides back in from the left side, etc. etc.

Hopefully that makes sense. I'm quite new to XAML and WPF so just trying to figure out how I would go about doing this. Any help or guidance would be much appreciated. Thanks!

alduin
  • 317
  • 3
  • 11
  • 1
    Probably create a `CollectionViewSource` of your background images, bind the background to the current item, use the `ICollectionView.MoveCurrentToNext` and `MoveCurrentToFirst` methods on button click. (This is just the static displaying part, it doesn't yet answer how to animate the fade in / fade out) – grek40 Jan 08 '19 at 06:21

2 Answers2

2

I would use a ListView and an ObservableCollection<string> in ViewModel. The ObservableCollection<string> contains a dynamic list of paths to the images. Be sure that the Build Action of the images is set to Resource. Then within the Background Property of Window place an ImageBrush where you bind the Source Property to the SelectedItem Property of ListView. The path strings of images follows a scheme you can find here: https://learn.microsoft.com/en-us/dotnet/framework/wpf/app-development/pack-uris-in-wpf

As desired (Images are BuildAction to Resource and copy if newer):

MainWindow.xaml

<Window x:Class="WinTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WinTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:TestViewModel x:Key="viewModel"/>
        <local:ImageConverter x:Key="converter"/>
    </Window.Resources>
    <Window.DataContext>
        <Binding Source="{StaticResource viewModel}" IsAsync="True"/>
    </Window.DataContext>
    <Window.Background>
        <ImageBrush ImageSource="{Binding SelectedImagePath, Converter={StaticResource converter}}"/>
    </Window.Background>
    <Grid Background="Transparent">
        <ListView Background="Transparent" SelectedValue="{Binding SelectedImagePath, Mode=TwoWay}" ItemsSource="{Binding PathList}"/>
    </Grid>
</Window>

TestViewModel.cs (Collection can used as string or Uri list. You have to instanciate a new Uri in Converter from value if you use strings)

public class TestViewModel : BasePropertyChangeNotification
{
    public ObservableCollection<Uri> PathList
    {
        get;
        private set;
    }

    public Uri SelectedImagePath
    {
        get { return this.selectedImagePath; }
        set { this.SetProperty(ref this.selectedImagePath, value); }
    }
    private Uri selectedImagePath = new Uri("pack://application:,,,/Images/img1.jpg", UriKind.RelativeOrAbsolute);

    public TestViewModel()
    {
        this.PathList = new ObservableCollection<Uri>
        {
            new Uri("pack://application:,,,/Images/img1.jpg", UriKind.RelativeOrAbsolute),
            new Uri("pack://application:,,,/Images/img2.jpg", UriKind.RelativeOrAbsolute),
            new Uri("pack://application:,,,/Images/img3.jpg", UriKind.RelativeOrAbsolute),
            new Uri("pack://application:,,,/Images/img4.jpg", UriKind.RelativeOrAbsolute),
            new Uri("pack://application:,,,/Images/img13.jpg", UriKind.RelativeOrAbsolute)
        };
    }
}

ImageConverter.cs

public class ImageConverter : IValueConverter
{
    public object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        return new BitmapImage(value as Uri);
    }

    public object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

That's all.

c0d3b34n
  • 534
  • 7
  • 14
  • The Buttons I would bind with Command to the viewmodel and set the selectedItem to the next or the previous item. – c0d3b34n Jan 08 '19 at 07:19
  • Is there a sample somewhere that elaborates a bit more on what you've suggested? I'm still pretty new to WPF so I'm not quite sure how to implement what you've described. – alduin Jan 09 '19 at 04:58
  • Thanks. It was the XAML side of things I was struggling with figuring out, specifically the syntax for the binding stuff. Is there an advantage to using the ObservableCollection instead of a CollectionViewSource as grek40 commented? Also, I'm having trouble inheriting BasePropertyChangeNotification for the ViewModel. It keeps telling me the type or namespace cannot be found and I can't figure out which namespace it belongs to. – alduin Jan 10 '19 at 01:24
  • BasePropertyChangeNotification is a simple implementation of INotifyPropertyChanged. SetProperty does nothing else than setting the reference of private var to value and call the PropertyChangedEvent. Binding an ObservableCollection to CollectionViewSource you can have a look here: https://stackoverflow.com/a/10091681/3506845 – c0d3b34n Jan 10 '19 at 08:18
  • @alduin `ObservableCollection` and `CollectionViewSource` are different beasts... use `ObservableCollection` in your **Viewmodel**, if you want to change the collection of items dynamically (load new items, remove items). My suggestion with `CollectionViewSource` was targeting the **View** part. It would be linked to the `ObservableCollection` or any other kind of background collection and it would be used to select the currently displayed image. – grek40 Jan 16 '19 at 06:40
0

That's how you can do this ! I have tested it . You can apply animations for carousel type effects.

public MainWindow()
{
    InitializeComponent();

    myImagesList = new List<ImageBrush>();
    ImageBrush myBrush1 = new ImageBrush(new BitmapImage(new Uri(@"C:\Users\Abdul Rehman\Desktop\1-Rao Hammas Folder\MY PROJECTS\StackOverFlowSolutions\StackOverFlowSolutions\Images\Capture.JPG")));
    ImageBrush myBrush2 = new ImageBrush(new BitmapImage(new Uri(@"C:\Users\Abdul Rehman\\Desktop\1-Rao Hammas Folder\MY PROJECTS\StackOverFlowSolutions\\StackOverFlowSolutions\Images\\Apps-Dialog-Close-icon.png")));
    ImageBrush myBrush3 = new ImageBrush(new BitmapImage(new Uri(@"C:\Users\Abdul Rehman\\Desktop\1-Rao Hammas Folder\MY PROJECTS\StackOverFlowSolutions\\StackOverFlowSolutions\Images\\Capture.JPG")));
    ImageBrush myBrush4 = new ImageBrush(new BitmapImage(new Uri(@"C:\Users\Abdul Rehman\\Desktop\1-Rao Hammas Folder\MY PROJECTS\StackOverFlowSolutions\\StackOverFlowSolutions\Images\\Capture.JPG")));
    ImageBrush myBrush5 = new ImageBrush(new BitmapImage(new Uri(@"C:\Users\Abdul Rehman\\Desktop\1-Rao Hammas Folder\MY PROJECTS\StackOverFlowSolutions\\StackOverFlowSolutions\Images\\Capture.JPG")));    

    myImagesList.Add(myBrush1);
    myImagesList.Add(myBrush2);
    myImagesList.Add(myBrush3);
    myImagesList.Add(myBrush4);
    myImagesList.Add(myBrush5);


    MainWin.Background = myImagesList[index];
}
private int index = 0;
private List<ImageBrush> myImagesList;

private void NextBtn_Click(object sender, RoutedEventArgs e)
{
    index++;
    MainWin.Background = myImagesList[index];
}

private void PrevBtn_Click(object sender, RoutedEventArgs e)
{
    index--;
    MainWin.Background = myImagesList[index];
}  

XAML

<Window x:Class="StackOverFlowSolutions.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Name="MainWin"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Name="NextBtn" Width="30" Height="20" Click="NextBtn_Click">Next</Button>
        <Button Name="PrevBtn" Width="30" Height="20" Margin="297,111,176,180" Click="PrevBtn_Click">Prev</Button>
    </Grid>
</Window>
Hammas_Stack1
  • 184
  • 1
  • 13
  • `BaseUriHelper.GetBaseUri(this)` is nonsense when you create BitmapImages from absolute file paths. – Clemens Jan 08 '19 at 09:11
  • @Clemens and you down voted just for this :( i did that quickly just to help this guy :( :( :( :( – Hammas_Stack1 Jan 08 '19 at 12:03
  • 1
    Sure, and I'd be glad to remove the downvote if you remove that stuff from your answer. Note also that you could prefix C# string literals with an `@` character to avoid the need for double backslashes. IMO, "*just [to] help this guy*" still requires a certain level of code quality. – Clemens Jan 08 '19 at 12:13
  • How does this work with the animations of the BG images? Do I need to have all of the images loaded off screen or something and then slide them in when appropriate? Your implementation works great for creating the images and changing them, but I'm not too sure of the next step to implement the desired animations. – alduin Jan 09 '19 at 04:48
  • @alduin happy that it worked for you bro ! applying animation is another question ! Please search google for WPF animations and you will get it . It's pretty easy. and if this solution worked for you then ill be happy if you mark it as correct answer ! Thanks – Hammas_Stack1 Jan 10 '19 at 04:53