Converted Value Not Shown In The UI of GridView - c#

I have a function that takes an object of storagefile and creates a thumbnail of it. This function is in the converter value since we have 10.000+ files that are going to be shown in a media gallery. The thumbnail will be generated on the file when the thumbnail is not yet created for the storage file.
The problem is, the UI of GridView is not updated once the thumbnail is generated / the object is updated. It will only show the thumbnail if I scroll down far enough and scroll back up again.
public class MediaFile : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ImageSource _thumbnail = null;
public ImageSource Thumbnail
{
get { return _thumbnail; }
set
{
_thumbnail = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Thumbnail");
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged(this, e);
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged(string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
This is the class of the so called MediaFile. As you can see I already implemented the property changed event.
<DataTemplate x:Key="GridMedia_DataTemplate" x:DataType="MediaFile">
<Grid Style="{StaticResource Faves_Grid}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Row="1" Grid.RowSpan="2" Margin="0 14 0 -12" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" Stretch="UniformToFill" VerticalAlignment="Stretch" Source="{x:Bind Converter={StaticResource ThumbnailGenerator}, Mode=OneWay}"/>
</Grid>
</DataTemplate>
Here is the data template for the grid view. As you can see, for the ImageSource we use converter to get / generate the thumbnail.
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
if(value is MediaFile)
{
MediaFile file = value as MediaFile;
//generate Thumbnail
int width = 160;
int height = 90;
var pathFileTemp = $#"{FileLocations.MediaPath}";
try
{
StorageFile originalFile = StorageFile.GetFileFromPathAsync(pathFileTemp + file.Id).AsTask().Result;
if (originalFile != null)
{
//check extention
if (file.Extension.ToLower().Contains("pdf"))
{
GetThumbnailForPDF(file).GetAwaiter();
return file.Thumbnail;
}
else if (file.Extension.ToLower().Contains("jpg") ||
file.Extension.ToLower().Contains("gif") ||
file.Extension.ToLower().Contains("png") ||
file.Extension.ToLower().Contains("jpeg"))
{
GetThumbnailForImage(file).GetAwaiter();
return file.Thumbnail;
}
}
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
catch (Exception)
{
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
}
return null;
}
catch (Exception)
{
return null;
}
}
As for now we only create thumbnail for pdf and images. Other than that we will return placeholder thumbnail.
<controls1:AdaptiveGridView Grid.Row="1" DesiredWidth="288" x:Name="ListView_MediaLibrary" Grid.ColumnSpan="4" ItemTemplate="{StaticResource GridMedia_Adaptive__DataTemplate}" Margin="0 0 -8 -20" SelectionChanged="ListView_MediaLibrary_SelectionChanged" ItemsSource="{Binding Source={StaticResource mediaFilesSource}, Mode=OneWay}">
</controls1:AdaptiveGridView>
And this is how we bind the list with the GridView.
Update
The thumbnail is visible only if I get out from the view by scrolling down, and then scrolling up
MediaFile.cs
public class MediaFile : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ImageSource _thumbnail = null;
public ImageSource Thumbnail
{
get { return _thumbnail; }
set
{
_thumbnail = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Thumbnail");
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged(this, e);
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged(string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public override bool Equals(object obj)
{
return obj is MediaFile mediaFile &&
Id == mediaFile.Id;
}
}
}
GridMedia.xaml UserControl
<UserControl>
<Grid Style="{StaticResource Faves_Grid}">
<Image HorizontalAlignment="Stretch" x:Name="ThumbnailImage" Stretch="UniformToFill" VerticalAlignment="Stretch"/>
</Grid></UserControl>
GridMedia.xaml.cs UserControl
public sealed partial class GridMedia : UserControl
{
public GridMedia()
{
this.InitializeComponent();
}
public MediaFile Data
{
get { return (MediaFile)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(MediaFile), typeof(GridMedia), new PropertyMetadata(null, new PropertyChangedCallback(Data_Changed)));
private static void Data_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null && !e.NewValue.Equals(e.OldValue) && e.NewValue is MediaFile data)
{
var instance = d as GridMedia;
// get a bitmapImage through the convert method
instance.ThumbnailImage.Source = Thumbnail(e.NewValue);
}
}
static ImageSource Thumbnail(object value)
{
try
{
if (value is MediaFile)
{
MediaFile file = value as MediaFile;
//generate Thumbnail
int width = 160;
int height = 90;
var pathFileTemp = $#"{FileLocations.MediaPath}";
try
{
StorageFile originalFile = StorageFile.GetFileFromPathAsync(pathFileTemp + file.Id).AsTask().Result;
if (originalFile != null)
{
//check extention
if (file.Extension.ToLower().Contains("pdf"))
{
GetThumbnailForPDF(file).GetAwaiter();
return file.Thumbnail;
}
else if (file.Extension.ToLower().Contains("jpg") ||
file.Extension.ToLower().Contains("gif") ||
file.Extension.ToLower().Contains("png") ||
file.Extension.ToLower().Contains("jpeg"))
{
GetThumbnailForImage(file).GetAwaiter();
return file.Thumbnail;
}
}
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
catch (Exception)
{
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
}
return null;
}
catch (Exception)
{
return null;
}
}

Please try to use Converter to handle simple conversion instead of asynchronous conversion involving IO.
You can try to convert your DataTemplate into UserControl, and convert the file operation logic of Converter into the callback of dependency property change inside the control.
Please try the following steps:
Create a comparison method for the MediaFile class (Suppose there is a unique Id property)
public class MediaFile : INotifyPropertyChanged
{
//... other code
public override bool Equals(object obj)
{
return obj is MediaFile mediaFile &&
Id == mediaFile.Id;
}
}
Create a UserControl
MyMediaFileItem.xaml
<UserControl
...>
<Grid>
<!-- your other code -->
<Image x:Name="ThumbnailImage"/>
</Grid>
</UserControl>
MyMediaFileItem.xaml.cs
public MediaFile Data
{
get { return (MediaFile)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(MediaFile), typeof(MyMediaFileItem), new PropertyMetadata(null,new PropertyChangedCallback(Data_Changed)));
private static void Data_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue!=null && !e.NewValue.Equals(e.OldValue) && e.NewValue is MediaFile data)
{
var instance = d as MyMediaFileItem;
// get a bitmapImage through the convert method
instance.ThumbnailImage.Source = bitmapImage;
}
}
Usage
<DataTemplate x:Key="GridMedia_DataTemplate" x:DataType="MediaFile">
<controls:MyMediaFileItem Data="{Binding}"/>
</DataTemplate>
Thanks.

Related

WPF Custom Control's Dependency Property unintentionally affecting other controls

I am making a window which is supposed to record the mouse position. I have created a user control which is supposed to display the current coordinates for that position tag and allow the user to change them.
It works great except for the fact that when one is updated they all change.
I think this has something to do with the fact that dependency properties are statically registered. They need to be dependency properties because I need to be able to Bind them to my model from the xaml.
How can I have the User Controls independent from one another?
<UserControl x:Class="SapFormFiller.SerializableMouseEditorControl"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60*"/>
...
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding LabelText}"></Label>
<Label Grid.Column="1" Content="{Binding SerializableMouseKeyboardEventArgs.X}"/>
<Label Grid.Column="2" Content="{Binding SerializableMouseKeyboardEventArgs.Y}"/>
<Button Grid.Column="3" Margin="0,0,0.4,0" Click="ButtonBase_OnClick">Edit</Button>
</Grid>
cs:
public partial class SerializableMouseEditorControl : UserControl
{
public static DependencyProperty LabelTextProperty = DependencyProperty.Register(
"LabelText", typeof (string), typeof (SerializableMouseEditorControl), new PropertyMetadata(default(string)));
public string LabelText
{
get { return (string) GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
public static DependencyProperty SerializableMouseKeyboardEventArgsProperty = DependencyProperty.Register(
"SerializableMouseKeyboardEventArgs", typeof (SerializableMouseKeyboardEventArgs), typeof (SerializableMouseEditorControl), new PropertyMetadata(new SerializableMouseKeyboardEventArgs()));
public SerializableMouseKeyboardEventArgs SerializableMouseKeyboardEventArgs
{
get { return (SerializableMouseKeyboardEventArgs) GetValue(SerializableMouseKeyboardEventArgsProperty); }
set { SetValue(SerializableMouseKeyboardEventArgsProperty, value); }
}
public SerializableMouseEditorControl()
{
InitializeComponent();
SerializableMouseKeyboardEventArgs = new SerializableMouseKeyboardEventArgs();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SerializableMouseKeyboardEventArgs.Update();
}
}
SerializableMouseKeyboardEventArgs:
public class SerializableMouseKeyboardEventArgs : INotifyPropertyChanged
{
public int X
{
get { return _x; }
set
{
_x = value;
OnPropertyChanged("X");
}
}
public int Y
{
get { return _y; }
set
{
_y = value;
OnPropertyChanged("Y");
}
}
...
IKeyboardMouseEvents gkme;
private int _x = 0;
private int _y=0;
public override string ToString(){...}
public SerializableMouseKeyboardEventArgs()
{
gkme = Hook.GlobalEvents();
}
public void Update()
{
IsEditing = true;
gkme.MouseClick += Gkme_MouseClick;
}
private void Gkme_MouseClick(object sender, MouseEventArgs e)
{
if(e.Button==MouseButtons.Left)
{
this.X = e.X;
this.Y = e.Y;
}
else if (e.Button == MouseButtons.Right)
{
gkme.MouseClick -= Gkme_MouseClick;
IsEditing = false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This line
new PropertyMetadata(new SerializableMouseKeyboardEventArgs()));
looks like it could be causing you problems. I was in a situation where declaring default values using new keyword was causing one instance of the UserControl to share that property value with another instance.
Try initializing any such properties in your constructor and it might work.

WPF UserControl property change not updating

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));
}
}
}

How do I update a listview when my variable changes?

I have a conditional style using a StyleSelector, so that it changes the color of the playing song to green when the program loads. However, when the songIndex static variable is changed, I don't know how to make it update. I tried using the INotifyPropertyChanged interface, but am not sure how to use it properly or what I am supposed to bind to it. Here is my code....
public class HighlightStyleSelector : StyleSelector, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected override Style SelectStyleCore(object item, DependencyObject container)
{
//List<myMediaInterface> mediaList = new List<myMediaInterface>();
if (item == MainPage.mediaList[MainPage.songIndex])
{
Style style = new Style(typeof(ListViewItem));
style.Setters.Add(new Setter(ListViewItem.BackgroundProperty, new SolidColorBrush(Colors.LightGreen)));
return style;
}
else
{
var style = Application.Current.Resources["ListViewItemStyle1"] as Style;
return null;
}
}
public int songIndex
{
get { return MainPage.songIndex; }
set
{
songIndex = MainPage.songIndex;
OnPropertyChanged(songIndex.ToString());
}
}
protected void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Xaml:
<ListView x:Name="songlistView" SelectionMode="Extended" DoubleTapped="songlistView_DoubleTapped" HorizontalContentAlignment="Stretch" BorderThickness="1" BorderBrush="#FF616161" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollMode="Enabled" ManipulationMode="None" UseLayoutRounding="False" VerticalContentAlignment="Stretch" Margin="2,150,0,558" Tapped="songlistView_Tapped" FontSize="14" ItemContainerStyleSelector="{StaticResource HighlightStyleSelector}" ItemsSource="{Binding MainPage.mediaList}">
Here is the code for the custom listview
namespace HelloWorld
{
public class MyListView : Control
{
public int highlightedItem;
public MyListView()
{
this.DefaultStyleKey = typeof(MyListView);
}
}
}
If I use get; and set; for hightlighted item doesn't work either. Still says the member highlightedItem is not recognized or is not accessible
Edited 5/25
this is now in MainPage.xaml.cs
public int songIndex
{
get
{
return songIndex;
}
set
{
songIndex = value;
OnPropertyChanged("songIndex");
}
}
^^ not sure if this should go with my field declarations?
public void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Here is my code from MainPage.xaml
<ListView x:Name="songlistView" SelectedIndex="{Binding songIndex}" SelectionMode="Extended" DoubleTapped="songlistView_DoubleTapped" HorizontalContentAlignment="Stretch" BorderThickness="1" BorderBrush="#FF616161" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollMode="Enabled" ManipulationMode="None" UseLayoutRounding="False" VerticalContentAlignment="Stretch" Margin="2,150,0,558" Tapped="songlistView_Tapped" FontSize="14" ItemsSource="{Binding MainPage.mediaList}"><!--ItemContainerStyleSelector="{StaticResource HighlightStyleSelector}"-->
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Here is my code 5/26
I'm trying to create a dependencyproperty called highlightedIndex that is identical to selectedIndex, except that it is separate.
public class MyListView : ListView
{
public int highlightedIndex
{
get { return (int)GetValue(HighlightedProperty); }
set
{
SetValue(HighlightedProperty, value);
}
}
public static readonly DependencyProperty HighlightedProperty = DependencyProperty.Register("HighlightedProperty", typeof(int), typeof(MyListView), new PropertyMetadata(0));
}
namespace HelloWorld
{
public class HighlightStyleSelector : StyleSelector
{
protected override Style SelectStyleCore(object item, DependencyObject container)
{
if (item == MainPage.mediaList[MainPage.songIndex])
{
var style = Application.Current.Resources["ListViewItemHighlighted"] as Style;
Setter setter = new Setter(ListViewItem.BackgroundProperty, new SolidColorBrush(Colors.LightGreen));
//Style style = new Style(typeof(ListViewItem));
style.Setters.Add(setter);
return style;
}
else
{
var style = Application.Current.Resources["ListViewItemStyle1"] as Style;
return style;
}
}
}
}
I'm a bit confused because of static properties on MainPage you seem to be assigning songIndex to and binding mediaList to. It would be helpful to see that code as well.
Still, you need to fix your property (assuming OnPropertyChanged is implemented correctly):
public int songIndex
{
get { return MainPage.songIndex; }
set
{
// set the assigned value to property backing field
MainPage.songIndex = value;
// you need to notify with the name of the property as the argument
OnPropertyChanged("songIndex");
}
}
Then you can bind to this property like any other with the only difference that the control will be notified when its value changes:
<ListView SelectedIndex="{Binding songIndex}" />
public static readonly DependencyProperty HighlightedProperty = DependencyProperty.Register("highlightedIndex", typeof(int), typeof(MyListView), new PropertyMetadata(null, propertyChanged));
private static void propertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
int newValue = (int)e.NewValue;
ListView lv = (ListView)d;
foreach (ListViewItem lvi in lv.Items)
{
if (lv.Items.IndexOf(lvi) == newValue)
{
lvi.Background = new SolidColorBrush(Colors.LightGreen);
}
else
{
lvi.Background = new SolidColorBrush();
}
}
}
Didn't need the styleselector or any binding

Either adressing a property in a sub class or a binding issue

I have a problem and I'm not sure what it is. I have a class within a class that has a value that needs to be bound to a control, in this case visibility. The code is changing the value correctly but the output does not change (i.e collapse the control)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Orientation="Vertical">
<Button x:Name="buttonOne" Content="Show Hide" Width="Auto" Click="buttonOne_Click"/>
<ListBox x:Name="aListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="blockOne" Grid.Column="0" Text="Raw "/>
<TextBlock x:Name="blockTwo" Grid.Column="1" Text="{Binding aValue}" Visibility="{Binding Path=visControl.VisibleState, BindsDirectlyToSource=True}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
public partial class MainPage : PhoneApplicationPage
{
private List<myClass> listOfClasses = new List<myClass>();
// Constructor
public MainPage()
{
myClass classA = new myClass("one");
myClass classB = new myClass("two");
myClass classC = new myClass("three");
listOfClasses.Add(classA);
listOfClasses.Add(classB);
listOfClasses.Add(classC);
InitializeComponent();
aListBox.ItemsSource = listOfClasses;
}
private void buttonOne_Click(object sender, RoutedEventArgs e)
{
foreach (myClass cl in listOfClasses)
if (cl.SwitchVisible)
cl.SwitchVisible = false;
else
cl.SwitchVisible = true;
}
}
public class myClass
{
private string _aValue;
private bool _switchVisible;
public bool SwitchVisible { get { return _switchVisible; } set { _switchVisible = value; visControl.changeVisibility(_switchVisible); } }
public string aValue { get { return _aValue; } }
public controlProperties visControl;
public myClass(string invalue)
{
visControl = new controlProperties();
visControl.VisibleState = Visibility.Visible;
_aValue = invalue;
}
}
public class controlProperties
{
private Visibility _visibility;
public Visibility VisibleState { get { return _visibility; } set { _visibility = value; } }
public void changeVisibility(bool isVisible)
{
if (isVisible)
_visibility = Visibility.Visible;
else
_visibility = Visibility.Collapsed;
}
}
Any ideas if this is a pathing issue or a binding problem?
If you want the control to be automatically updated when you change the value of the property, your class must implement the INotifyPropertyChanged interface.
For instance:
public class controlProperties : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Visibility _visibility;
public Visibility VisibleState
{
get
{
return _visibility;
}
set
{
_visibility = value;
this.NotifyPropertyChanged("VisibleState");
}
}
public void changeVisibility(bool isVisible)
{
if (isVisible)
this.VisibleState = Visibility.Visible;
else
this.VisibleState = Visibility.Collapsed;
}
private void NotifyPropertyChanged(string propertyName)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(sender, new PropertyChangedEventArgs(propertyName));
}
}
}

how to retrived and save selected image to/from the listbox to/from isolatedstorage

I am retriving images from the media library to list box inside a wrap panel now I want to save the images selected (its a multiple selection listbox) to the isolatedstorage.
xaml of listbox
<ListBox Name="vaultbox" SelectionMode="Multiple"
ItemContainerStyle="{StaticResource ListBoxItemStyle1}">
<TextBlock Text="It is so lonely here..." Visibility="Collapsed" />
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel ItemWidth="200" ItemHeight="200"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Name="image2"
Stretch="Fill"
VerticalAlignment="Top" Source="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am lost here. I was trying to do this.
List<BitmapImage> vltBitmapImage = new List<BitmapImage>();
foreach (string fileName in fileStorage.GetFileNames("images//*.*"))
{
if (fileName == null)
break;
string filepath = System.IO.Path.Combine("images", fileName);
using(IsolatedStorageFileStream imageStream =
fileStorage.OpenFile(filepath,FileMode.Open,FileAccess.Read))
{
var imageSource=PictureDecoder.DecodeJpeg(imageStream);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
vltBitmapImage.Add(bitmapImage);
}
}
this.vaultbox.ItemsSource = vltBitmapImage;
using above code i get this exception
'System.Invalid.Operation.Exception Items collection must be empty
before using ItemsSource'
dont know why its the same code almost from what i am showing pictures from media library to the listbox.
also from a similar list box above but different one i try to save the files to isolatedstorage but i can seem to find out how can i get the image name...
see here. Currently am using "name" what can I do for that?
foreach (BitmapImage item in lstImageFromMediaLibrary.SelectedItems)
{
string filepath =System.IO.Path.Combine("images", "name");
IsolatedStorageFileStream ifs = fileStorage.CreateFile(filepath);
{
var bmp = new WriteableBitmap(item);
bmp.SaveJpeg(ifs,item.PixelWidth,item.PixelHeight,0,90);
}
}
The exception occures because of this line:
<TextBlock Text="It is so lonely here..." />
You have a child of ListBox. And you try to add more.
I would like to recommend you using MVVM pattern: create the ViewModel and put the collection property inside. Also, it it very convenient to have the "selected item" property in this ViewModel class and bind the selected item of the ListBox to it.
It seems it is necessary to introduce the image item representation: it is image with name.
public class ImageViewModel : ViewModelBase
{
public ImageViewModel(string name, string image)
{
Name = name;
Image = image;
}
// Name or Path? Please make the choice by yourself! =)
public string Name { get; private set; }
public BitmapImage Image { get; private set; }
}
ViewModelBase class (implements INotifyPropertyChanged interface):
public abstract class ViewModelBase : INotifyPropertyChanged
{
protected ViewModelBase()
{
}
#region Implementation of INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
var args = new PropertyChangedEventArgs(propertyName);
handler(this, args);
}
}
#endregion
}
The main (root) view model.
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
// Call this directly:
// var images = LoadImages();
// Images = images;
// or bind the Command property of Button to the LoadImagesCommand.
LoadImagesCommand = new RelayCommand((param) =>
{
var images = LoadImages();
Images = new ObservableCollection<ImageViewModel>(images);
});
}
private ObservableCollection<ImageViewModel> _images;
public ObservableCollection<ImageViewModel> Images
{
get { return _images; }
private set
{
if (value == _images)
return;
_images = value;
RaisePropertyChanged("Images");
}
}
private ImageViewModel _selectedImage;
public ImageViewModel SelectedImage
{
get { return _selectedImage; }
set
{
if (value == _selectedImage)
return;
_selectedImage = value;
RaisePropertyChanged("SelectedImage");
}
}
public ICommand LoadImagesCommand { get; private set; }
private List<ImageViewModel> LoadImages()
{
List<ImageViewModel> images = new List<ImageViewModel>();
// Part of the original code.
foreach (string fileName in fileStorage.GetFileNames("images//*.*"))
{
if (fileName == null)
break;
string filepath = System.IO.Path.Combine("images", fileName);
using (IsolatedStorageFileStream imageStream = fileStorage.OpenFile(filepath,FileMode.Open,FileAccess.Read))
{
var imageSource = PictureDecoder.DecodeJpeg(imageStream);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
ImageViewModel imageViewModel = new ImageViewModel(fileName, bitmapImage);
images.Add(imageViewModel);
}
}
}
}
Window.
public class MainView : Window
{
public MainView()
{
InititializeComponent();
// It is just for simplicity. It would be better to use MVVM Light framework: ViewModel Locator!
DataContext = new MainViewModel();
}
}
XAML:
<!-- Please correct the binding for the Image property inside the ItemStyle -->
<ListBox Name="vaultbox"
ItemsSource="{Binding Images}"
SelectedItem={Binding SelectedImage}"
ItemContainerStyle="{StaticResource ListBoxItemStyle1}"
Height="493"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Width="442"
SelectionMode="Multiple">
...
</ListBox>
Also, please consider asynchronous loading: it won't freeze the UI.

Categories