Listview Selected Item as Image Source - c#

I have a listview in my WPF app. Each item in the listview is a path of an image file. I'd like to show a thumbnail or preview of selected image on the listview.
MainWindow.xaml
<Image Source="{Binding SelectedImage}" />
<ListView x:Name="MyListView" AllowDrop="True" Drop="MyListView_Drop" />
MainWindow.xaml.cs
namespace MyWPFApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyDataContext();
}
}
private void MyListView_Drop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
List<string> fileList = new List<string>(files);
foreach (string file in fileList)
{
if ((file.ToLower().EndsWith(".jpg") || file.ToLower().EndsWith(".jpeg") && !MyListView.Items.Contains(file))
{
MyListView.Items.Add(file);
}
}
}
public class MyDataContext : INotifyPropertyChanged
{
public string SelectedImage
{
get { return ((KeyValuePair<string, object>?)((MainWindow)Application.Current.MainWindow).MyListView.SelectedItem)?.Value?.ToString(); }
set
{
((MainWindow)Application.Current.MainWindow).MyListView.SelectedItem = value;
OnPropertyChanged(nameof(SelectedImage));
}
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Currently, selecting an image on the listview does not do anything. Any help will be greatly appreciated.

Your ListView does not bind to the SelectedImage property.
Besides that, you should consider having an actual view model with an ObservableCollection property for the image files list in addition to the SelectedImage property.
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> Images { get; }
= new ObservableCollection<string>();
private string selectedImage;
public string SelectedImage
{
get { return selectedImage; }
set
{
selectedImage = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedImage)));
}
}
}
You would bind to it in XAML like shown below (perhaps with a ListBox instead of a ListView, because it is simpler):
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Images}"
SelectedItem="{Binding SelectedImage}"
AllowDrop="True" Drop="ListView_Drop"/>
<Image Grid.Column="1" Source="{Binding SelectedImage}" />
</Grid>
The code behind of the window:
public partial class MainWindow : Window
{
private readonly ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
private void ListView_Drop(object sender, DragEventArgs e)
{
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (var file in files.Where(
f => (f.ToLower().EndsWith(".jpg") || f.ToLower().EndsWith(".jpeg"))
&& !viewModel.Images.Contains(f)))
{
viewModel.Images.Add(file);
}
}
}

Related

How do I properly set my itemsource in WPF to display my ObservableCollection items?

I have this project where I am trying to figure out how to add things to the listview and then later be able to delete them. But I cant seem to get the itemsource to bind unless I hardcode it. How do I do it in the XAML?
This adds a item to the listview
public partial class MainWindow : Window
{
private ObservableCollection<myItem> Item;
const string pattern = #"((.*)) (.*) left the game";
public MainWindow()
{
InitializeComponent();
}
private void btnAppend_Click(object sender, RoutedEventArgs e)
{
Item = new ObservableCollection<myItem>() { new myItem() { Username = "Prabhat" } };
lvUsers.ItemsSource = Item;
}
}
However if I remove the hardcoded itemsource it doesnt add it even if I add Itemsource={Binding Item} to ym XAML
<Grid>
<ListView Name="lvUsers" ItemsSource="{Binding Item}" HorizontalAlignment="Left" Height="107" Margin="10,10,0,0" VerticalAlignment="Top" Width="497">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="100" DisplayMemberBinding="{Binding Username}"/>
</GridView>
</ListView.View>
</ListView>
<TextBox Name="tbConent" HorizontalAlignment="Left" Height="78" Margin="10,122,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="497"/>
<Button Name="btnAppend" Click="btnAppend_Click" Content="Append" HorizontalAlignment="Left" Margin="213,220,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
EDIT
Class
public class myItem
{
public string Username { get; set; }
}
EDIT 2
MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
namespace Listviewssssssssssssss
{
public partial class MainWindow : Window
{
public ObservableCollection<myItem> Item { get; private set; }
public MainWindow()
{
InitializeComponent();
}
private void btnAppend_Click(object sender, RoutedEventArgs e)
{
Item = new ObservableCollection<myItem>() { new myItem() { Username = "Prabhat" } };
lvUsers.Items.Refresh();
}
private void btnRemove_Click(object sender, RoutedEventArgs e)
{
}
}
}
Make Item a public property:
public ObservableCollection<myItem> Item { get; private set; }
And you can bind to it like this:
<ListView Name="lvUsers" ItemsSource="{Binding Item, RelativeSource={RelativeSource AncestorType=Window}}" ...>
Make sure that myItem is a public type:
public class myItem { ... }
Or you could set the DataContext of the window to itself:
public partial class MainWindow : Window
{
public ObservableCollection<myItem> Item { get; private set; }
const string pattern = #"((.*)) (.*) left the game";
public MainWindow()
{
InitializeComponent();
DataContext = this;
Item = new ObservableCollection<myItem>() { new myItem() { Username = "Prabhat" } };
}
}
...and bind to the source collection directly:
<ListView Name="lvUsers" ItemsSource="{Binding Item}" ...>
You should also probably consider renaming the property to "Items" since it is a collection.
Edit:
Edit: If you intend to set the Item property dynamically after the initial binding, you also need to implement the INotifyPropertyChanged interface and raise change notifications:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<myItem> _items;
public ObservableCollection<myItem> Item
{
get { return _items; }
set { _items = value; NotifyPropertyChanged(); }
}
public MainWindow()
{
InitializeComponent();
}
private void btnAppend_Click(object sender, RoutedEventArgs e)
{
Item = new ObservableCollection<myItem>() { new myItem() { Username = "Prabhat" } };
}
private void btnRemove_Click(object sender, RoutedEventArgs e)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Unable to check all / select all items in a CheckListBox in WPF [duplicate]

I have a UserControl that I add to my main application.
That UserControl contains a button for a UIElement
The UserControl contains a DispatchTimer and every 2 seconds based on some int values determines what the button image will be.
One of the methods called in the UserControl should set it's image but the control never displays the image that it was changed to.
public void SetNormal()
{
btnFlashAlert.Content = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
}
Is there something i'm missing to get the look of the control update on the main application?
When I look at what .Content contains, it is correct. The UI doesn't reflect the change.
XAML
<UserControl x:Class="SC.FlashSystem.MainButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="53" Width="164">
<Button x:Name="btnFlashAlert" Background="{x:Null}" BorderBrush="{x:Null}" Cursor="Hand" Click="btnFlashAlert_Click">
<Button.Template>
<ControlTemplate>
<Image Source="Images/FlashButton.png"/>
</ControlTemplate>
</Button.Template>
</Button>
Codebehind Updated
public partial class MainButton : UserControl
{
private SupportConsoleWeb.MessageData messageCounts { get; set; }
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
private BitmapImage NormalImage { get; set; }
private BitmapImage CriticalImage { get; set; }
private BitmapImage AlertImage { get; set; }
private BitmapImage InfoImage { get; set; }
public MainButton()
{
InitializeComponent();
messageCounts = new SupportConsoleWeb.MessageData();
messageCounts.CriticalCount = 0;
messageCounts.AlertCount = 0;
messageCounts.InfoCount = 0;
NormalImage = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
CriticalImage = new BitmapImage(new Uri("Images/FlashButtonRed.png", UriKind.RelativeOrAbsolute));
AlertImage = new BitmapImage(new Uri("Images/FlashButtonOrange.png", UriKind.RelativeOrAbsolute));
InfoImage = new BitmapImage(new Uri("Images/FlashButtonGreen.png", UriKind.RelativeOrAbsolute));
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += flashButtonChangeTimer_Tick;
flashButtonChangeTimer.Start();
}
void flashButtonChangeTimer_Tick(object sender, EventArgs e)
{
btnFlashAlert.Dispatcher.BeginInvoke(new Action(() =>
{
if (btnFlashAlert.Content == null)
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0 && btnFlashAlert.Content.Equals(CriticalImage))
{
SetNormal();
}
else if (messageCounts.AlertCount > 0 && btnFlashAlert.Content.Equals(AlertImage))
{
SetNormal();
}
else if (messageCounts.InfoCount > 0 && btnFlashAlert.Content.Equals(InfoImage))
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0)
{
SetCritical();
}
else if (messageCounts.AlertCount > 0)
{
SetAlert();
}
else if (messageCounts.InfoCount > 0)
{
SetInfo();
}
}));
}
public void UpdateMessageCounts(SupportConsoleWeb.MessageData messageCounts)
{
this.messageCounts = messageCounts;
}
private void btnFlashAlert_Click(object sender, RoutedEventArgs e)
{
MainWindow window = new MainWindow();
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
window.ShowDialog();
}
public void SetMessageCount(int criticalCount, int alertCount, int infoCount)
{
messageCounts.CriticalCount = criticalCount;
messageCounts.AlertCount = alertCount;
messageCounts.InfoCount = infoCount;
}
private void SetNormal()
{
btnFlashAlert.Content = NormalImage;
}
private void SetCritical()
{
btnFlashAlert.Content = CriticalImage;
}
private void SetAlert()
{
btnFlashAlert.Content = AlertImage;
}
private void SetInfo()
{
btnFlashAlert.Content = InfoImage;
}
}
Change your XAML To this
<Image Source="{Binding TheImage}"/>
Add notify property changed
public partial class MainButton : UserControl, INotifyPropertyChanged
Create the OnPropertyChanged Event
void OnPropertyChanged(String prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Create a Bitmap prop and notify the prop changed event
private BitmapImage _TheImage;
public BitmapImage TheImage
{
get { return _TheImage; }
set { _TheImage = value; OnPropertyChanged("TheImage"); }
}
In your initializer
public MainButton()
{
this.DataContext = this;
InitializeComponent();
TheImage = new BitmapImage();
Now in your setting methods call
TheImage = //Your Bitmap Goes here
I know this seems excessive but you will see it is a much cleaner implementation in the long run.
I believe its an issue with picture selection logic not having a default image when none of the conditions are met...
With that said, IMHO the picture logic would be better expressed by having all images pre-loaded and their visibility initially set to hidden. Then bind the visibility of each image to a specific flag boolean on the VM. Which the timer event can simply turn on or off the boolean(s) which will ultimately show or hide images as needed.
That removes any latency due to loading and showing of images for they will be pre-loaded; also it will solve any possible future memory issues due to loading/unloading of images.
Example
The following example has a button with two images. Both image's visibility is bound to Booleans on the VM. The VM has one Boolean which the imageas work off of and a timer which changes its status every two seconds switching the images.
Xaml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Button x:Name="bStatus" Width="48" Height="48">
<StackPanel Orientation="Vertical">
<Image Source="Images\Copy-icon.png" Visibility="{Binding IsCopyOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<Image Source="Images\Recycle-icon.png"
Visibility="{Binding IsRecycleOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</Button>
VM
public class MainVM : INotifyPropertyChanged
{
private bool _bSwitch;
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
public bool IsRecycleOn
{
get { return _bSwitch; }
}
public bool IsCopyOn
{
get { return !_bSwitch; }
}
public MainVM()
{
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += (sender, args) =>
{
_bSwitch = ! _bSwitch;
OnPropertyChanged("IsCopyOn");
OnPropertyChanged("IsRecycleOn");
};
flashButtonChangeTimer.Start();
}
/// <summary>Event raised when a property changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Raises the PropertyChanged event.</summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Seleceted Value from a ListBox WPF .NET 3.5

I have a listbox in my WPF app. The definition is given below:
<ListBox Margin="17.493,33.32,22.491,26.656" Name="lstData"
PreviewMouseLeftButtonDown="ListBox_MouseDown"
IsTextSearchEnabled="False" />
In the code behind, I bind the ListBox to a List. When the value is selected from the List Box, in my code behind, I want to be able to retrieve that value. How do I do it? Sample C# code will be helpful.
Thanks.
You can just bind to an item in your code behind
Example:
<ListBox Margin="17.493,33.32,22.491,26.656" Name="lstData"
SelectionChanged="ListBox_selectionChanged"
IsTextSearchEnabled="False"
ItemsSource="{Binding MyItems}"
SelectedItem="{Binding MySelectedItem}"/>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private ObservableCollection<MyItemType> _myItems = new ObservableCollection<MyItemType>();
public ObservableCollection<MyItemType> MyItems
{
get { return _myItems; }
set { _myItems = value; }
}
private MyItemType _mySelectedItem;
public MyItemType MySelectedItem
{
get { return _mySelectedItem; }
set { _mySelectedItem = value; NotifyPropertyChanged("MySelectedItem"); }
}
private void ListBox_selectionChanged(object sender, SelectionChangedEventArgs e)
{
MessageBox.Show(_mySelectedItem);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}

Can't able to perform button click and combo box selection changed in WinRT MVVM

UPDATE 1 : You can download the sample project from here.
Can you please help me to find the error in my code. I can't able to assign item source to combo box as well as button click event in WinRT app. I am using MVVM and MetroEventToCommand. I am new to MVVM concept, so please answer my silly question.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Click Here">
<mvvm:EventToCommandManager.Collection>
<mvvm:EventToCommand Command="{Binding ButtonClickCommand}" Event="Click"/>
</mvvm:EventToCommandManager.Collection>
</Button>
<ComboBox x:Name="FontsCombo" Height="50" Width="150" SelectedItem="{Binding SelectedFont}" ItemsSource="{Binding fonts}" />
<TextBlock FontSize="30" Text="{Binding SelectedFont}"/>
</Grid>
public MainPage()
{
this.InitializeComponent();
this.DataContext = new VM();
}
public class VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public RelayCommand ButtonClickCommand { get; set; }
private ObservableCollection<string> _fonts = new ObservableCollection<string>();
public ObservableCollection<string> fonts
{
get { return _fonts; }
set
{
_fonts = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("fonts"));
}
}
}
private string _SelectedFont = "";
public string SelectedFont
{
get { return _SelectedFont; }
set
{
// Some logic here
_SelectedFont = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedFont"));
}
}
}
public VM()
{
fonts.Add("Arial");
fonts.Add("Courier New");
fonts.Add("Times New Roman");
ButtonClickCommand = new RelayCommand(Click);
}
private void Click()
{
new Action(async () => await new Windows.UI.Popups.MessageDialog("Testing dialog").ShowAsync()).Invoke();
}
}
For the SelectedItem, you didn't specify the Mode=TwoWay :
<ComboBox x:Name="FontsCombo" Height="50" Width="150" SelectedItem="{Binding SelectedFont, Mode=TwoWay}" ItemsSource="{Binding fonts}" />
EDIT
I found the solution :
public class VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public RelayCommand ButtonClickCommand { get; set; }
private ObservableCollection<string> _fonts;
public ObservableCollection<string> fonts
{
get { return _fonts; }
set
{
_fonts = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("fonts"));
}
}
}
private string _SelectedFont;
public string SelectedFont
{
get { return _SelectedFont; }
set
{
// Some logic here
_SelectedFont = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedFont"));
}
}
}
public VM()
{
this.fonts = new ObservableCollection<string>();
fonts.Add("Arial");
fonts.Add("Courier New");
fonts.Add("Times New Roman");
ButtonClickCommand = new RelayCommand(Click);
}
private void Click()
{
new Action(async () => await new Windows.UI.Popups.MessageDialog("Testing dialog").ShowAsync()).Invoke();
}
}
If I instance fonts in the constructor, the UX is not freezing anymore.

Recursive databinding on treeview

I want to view a folder structure in a treeview using a databinding.
The folder class just has a property list of children and a property name.
If something changes it will fire the according event.
This is it:
public class Folder : INotifyPropertyChanged, INotifyCollectionChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public Folder(string name)
{
this.Name = name;
this.ContentFolders = new List<Folder>();
}
public List<Folder> ContentFolders { get; set; }
public void AddFolder(Folder f)
{
this.ContentFolders.Add(f);
if (this.CollectionChanged != null)
{
this.NotifyCollectionChanged(
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add, f));
}
this.PropertyChanged(this, new PropertyChangedEventArgs("ContentFolders"));
}
private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
{
lock (CollectionChanged)
{
if (CollectionChanged != null)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => CollectionChanged(this, e)));
}
}
}
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if (this.name != value)
{
this.name = value;
if (PropertyChanged != null)
{
PropertyChanged(
this, new PropertyChangedEventArgs("Name"));
}
}
}
}
}
This is my GUI, which shows the root folder in a treeview:
<Window x:Class="WpfApplication2.MyWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication2="clr-namespace:WpfApplication2"
Title="MyWindow" Height="300" Width="300" xmlns:u="clr-namespace:UpdateControls.XAML;assembly=UpdateControls.XAML">
<StackPanel>
<StackPanel.Resources>
<HierarchicalDataTemplate DataType="{x:Type WpfApplication2:Folder}"
ItemsSource="{Binding Path=ContentFolders}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
</StackPanel.Resources>
<TreeView Name="TreeviewScenario">
<TreeViewItem Header="{Binding Path=RootFolder.Name}"
ItemsSource="{Binding Path=RootFolder.ContentFolders}" />
</TreeView>
<Button Content="Add Folder" Click="Button_Click" />
</StackPanel>
</Window>
The according MyWindow.xaml.cs class has a property Folder and adds some content. It has also a method for the button to add a new folder if it becomes clicked.
public partial class MyWindow : Window
{
public Folder RootFolder { get; set; }
public MyWindow()
{
this.RootFolder = new Folder("root");
this.RootFolder.ContentFolders.Add(new Folder("1"));
this.RootFolder.ContentFolders.Add(new Folder("12"));
this.RootFolder.ContentFolders.Add(new Folder("13"));
this.RootFolder.ContentFolders.Add(new Folder("14"));
this.RootFolder.ContentFolders.Add(new Folder("15"));
Folder aFolder = new Folder("aFolder");
aFolder.ContentFolders.Add(new Folder("2"));
aFolder.ContentFolders.Add(new Folder("21"));
aFolder.ContentFolders.Add(new Folder("22"));
aFolder.ContentFolders.Add(new Folder("23"));
aFolder.ContentFolders.Add(new Folder("24"));
this.RootFolder.ContentFolders.Add(aFolder);
this.DataContext = this;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Folder c = new Folder("a new Folder");
this.RootFolder.AddFolder(c);
}
}
The Gui will be calles by a simple Main method with:
new MyWindow();
If I start, the treeview looks fine, it has all the items, which have been added in the MyWindow.xaml.cs.
But If I click the button, no new items will be shown. If I click the button before I expand the treeview, the new items will be there...
So the view seems not to be updated...
Can anybody see, what I have done wrong?
Change the ContentFolders in your Folder class to an ObservableCollection<> instead of a List<>
public ObservableCollection<Folder> ContentFolders { get; set; }

Categories

Resources