WPF OnMouseEnter change image using events - wpf

Is it possible to access a specific resource in the xaml using events?
I'm trying to have the image change when mouse enters a grid, but the problem is I don't have access to change the image from the event.
I want to do something like:
OnMouseEnter(object sender, MouseEventArgs e) {
((Image)GetResource("logo")).Source = "pathToImage.jpg";
}
Where 'logo' is the name of the image resource.

It seems you're just trying to set the Source property of an Image element declared somewhere in your XAML. All you need to do is to set the x:Name attribute, which will generate a member in your Window class, like
<Image x:Name="image"/>
Now you directly access the element in code behind, and assign a value to its Source property like this:
image.Source = new BitmapImage(new Uri("pathToImage.jpg"));

If the element you are attaching the OnMouseEnter on is a FrameworkElement then you can try do something like the following:
OnMouseEnter(object sender, MouseEventArgs e) {
var element = sender as FrameworkElement;
var image = element.FindResource("logo") as Image;
image.Source = new BitmapImage(new Uri("pathToImage.jpg"));
}
Otherwise you may have to change your "resource" from:
<Image x:Key="logo" Source="initialImage.jpg" />
to something like:
<Image x:Key="logo" Source="{Binding Source={x:Static LogoImage.SingletonInstance}, Path=Logo}" />
And have a class something along the lines of:
public class LogoImage : INotifyPropertyChanged {
public static LogoImage SingletonInstance { get; } = new LogoImage();
public ImageSource Logo { get; private set; }
public void SetLogo(ImageSource image)
{
Logo = image;
RaiseNotifyPropertyChanged(nameof(Logo));
}
// Implement INotifyPropertyChanged
}

Related

WPF listboxitem PreviewKeyDown

I have a listbox that is bound to a list of custom objects. I can get the listbox items to display correctly using the ListBox.ItemTemplate in xaml. The custom objects for the listbox are all of the same base class outlined below.
public class HomeViewMenuItem : UIElement
{
private Uri _uri;
private IRegionManager _manager;
public HomeViewMenuItem(string text, Uri uri, IRegionManager manager)
{
this.PreviewMouseDown += HomeViewMenuItem_PreviewMouseDown;
this.PreviewKeyDown += HomeViewMenuItem_PreviewKeyDown;
_manager = manager;
Text = text;
_uri = uri;
ClickCommand = new DelegateCommand(this.Click, this.CanClick);
}
void HomeViewMenuItem_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
{
e.Handled = true;
this.ClickCommand.Execute();
}
}
void HomeViewMenuItem_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
this.ClickCommand.Execute();
}
private void Click()
{
_manager.Regions[RegionNames.MainRegion].RequestNavigate(_uri);
}
private bool CanClick()
{
return true;
}
public DelegateCommand ClickCommand { get; set; }
public string Text { get; set; }
}
The problem I am having is the HomeViewMenuItem_PreviewKeyDown method is not getting called. I believe this is because the method is getting called on the ListBoxItem itself first and getting handled there. I was able to verify this by obtaining a reference to the ListBoxItem object through listBox.ItemContainerGenerator.ContainerFromIndex(0) after the ItemContainerGenerator status changes to ContainersGenerated and adding an event handler there. This event handler correctly fired. Normally this would be an ok solution on a small project but I plan on having more listboxes with the same sort of functionality and would like to have a simpler/better solution. Is there a way that I can get my base class previewkeydown method to work?
The only solution I could think of is to have the base class inherit from ListBoxItem instead of UIElement then get the ListBox to create my items instead of ListBoxItems. But I dont think that is really possible without creating my own ListBox implementation.
You seem to be somewhat confused. In WPF, we create data items and declare DataTemplates to define what those items should look like in the UI. Our data items do not extend UI classes. If you have to handle the PreviewKeyDown event, then attach a handler to the UI element in the DataTemplate instead:
<DataTemplate>
<Grid PreviewKeyDown="HomeViewMenuItem_PreviewKeyDown">
...
</Grid>
</DataTemplate>

Custom Button with images per state

I'm trying to create a custom usercontrol that acts like a button, but i want it to use different images for each state (Normal, Hover, Pressed). The user control contains an Image control to show the image. I want to change the Source of the Image control at-runtime, so when the OnMouseEnter event triggers, i would change my image source to the HoverChange (ImageSource) property.
So i tried to add 3 ImageSource properties (NormalState, HoverState and PressedState) to the usercontrol so i can change the images when needed. (Coming from WinForms) But the problem is that the Properties aren't set in code (WinForms behaviour), so i can't assign them to my image. But when i use the usercontrol in my program i can set them via the property panel, but i can't use them in code (they stay NULL).
Here is some (pseudo) code of what i'm trying to reach:
public partial class ThreeStateButton : UserControl
{
public enum ButtonState
{
Normal,
Hover,
Pressed
}
public ImageSource NormalState { get; set; }
public ImageSource HoverState { get; set; }
public ImageSource PressState { get; set; }
public ThreeStateButton()
{
InitializeComponent();
SetState(ButtonState.Normal);
}
public void SetState(ButtonState state)
{
switch (state)
{
case ButtonState.Normal:
imgButton.Source = NormalState;
break;
case ButtonState.Hover:
imgButton.Source = HoverState;
break;
case ButtonState.Pressed:
imgButton.Source = PressState;
break;
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
SetState(ButtonState.Hover);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
SetState(ButtonState.Normal);
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
SetState(ButtonState.Pressed);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
SetState(ButtonState.Hover);
}
}
The main problem is that the ImageSource properties aren't set after initialization (and yes there are set through the property panel in the editor), i know the way of working is a bit different in WPF but how could i get this working the way i try?
Thanks
I did it on a more WPF-style way.
I create 3 image source properties and for each of them i created a DependencyProperty. I also added 3 Image controls in my xaml code and binded those imagesource properties using their registered name as DependencyProperty to the imagesource property of my image controls. Then it's just a matter of hiding the images or setting them visible on the correct state.

Image source not updating dynamically

I have an Image that I have bound the 'Source' property of to the page's corresponding 'ViewModel' property named 'Capture' as follows:
View Code:
<Image Height="150" HorizontalAlignment="Left" Margin="50,583,0,0" x:Name="img_FlickrPic" Stretch="Fill" VerticalAlignment="Top" Width="200" Grid.Row="1" Source="{Binding Capture}"/>
Corresponding ViewModel code
public class SubmitViewModel : ViewModelBase
{
private CameraCaptureTask cameraCapture;
public ImageSource Capture { get; set; }
public RelayCommand CaptureCommand { get; set; }
/// <summary>
/// Initializes a new instance of the SubmitViewModel class.
/// </summary>
public SubmitViewModel()
{
Capture = new BitmapImage();
CaptureCommand = new RelayCommand(() => CapturePhoto());
}
private object CapturePhoto()
{
cameraCapture = new CameraCaptureTask();
cameraCapture.Completed += cameraCapture_Completed;
cameraCapture.Show();
return null;
}
void cameraCapture_Completed(object sender, PhotoResult e)
{
if (e == null || e.TaskResult != TaskResult.OK)
{
return;
}
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.ChosenPhoto);
Capture = bitmap;
}
}
As you notice, I have bound a capture button to the view model as well using 'behavior's instead of a click event in order to keep the code behind clean. When I hit the button, the camera gets invoked and once I hit capture and then press the 'Accept' button the 'cameraCapture_Completed' event fires as expected and the code in there executes. However the last step where the 'Capture' property (which my Image's Source property on the view is bound to) is set, I expect the Image to dynamically update with the captured photo. This does not happen. The viewmodel inherits from 'ViewModelBase' which in turn implements INotifyPropertyChanged, so that shouldn't be a problem. Why aren't any modifications to the 'Capture' property being reflected by the Image in the UI? Am I messing up somewhere here?
Thanks!
You have to manually raise the ChangedPropertyEvent with property name "Capture". Something like this.
ImageSource mCapture;
public ImageSource Capture {
get {return mCapture;}
set { mCapture = value;
RaiseChangedProperty("Capture");
}
}

Providing an initial image placeholder for the WPF Image class

When i run the project a runtime error ocure:
Error: Property 'UriSource' or property 'StreamSource' must be set.
because this.ImageUri is null , i don't know why this.ImageUri be null ! help me
I have been working with the WPF ListBox using images as my list box items. The sourced image path points to a server hosting those images. While on fast network, the images appeared without any noticeable delay. However it became apparent over a slow link that the user experience degraded and I really wanted to show a placeholder image while the image was downloaded and decoded.
Surprisingly, I didn't find a solution in the blogosphere for this issue so I coded up a derived class to address this.
The sample XAML below is from my item container style. I replaced Image with my local class implementation local:ImageLoader.
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyData}">
...
<StackPanel Grid.Column="0" Margin="5">
<Border BorderThickness="0">
<MyControl:ImageLoader Width="50" Height="50" ImageUri="{Binding Path=profile_image_url_https, FallbackValue=profile_image_url_https}" InitialImage="/MyProject;component/Images/nopic.png" HorizontalAlignment="Left"></imgz:ImageLoader>
</Border>
</StackPanel>
...
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source = {StaticResource MyData}}" />
</Grid>
The heart of the handling for the initial image is in the OnLoaded() method, where I use a BitmapImage as the source and set the UriSource to the derived class' ImageUri dependency property, which allows for data binding. The initial image is updated to the actual image when the download completes or when a failure event is received. The class also optionally allows you to specify a "LoadFailedImage".
public class ImageLoader : Image
{
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(null, null));
private BitmapImage loadedImage;
public ImageLoader()
{
this.Loaded += this.OnLoaded;
}
public string LoadFailedImage
{
get;
set;
}
public Uri ImageUri
{
get {return this.GetValue(ImageUriProperty) as Uri;}
set {this.SetValue(ImageUriProperty, value);}
}
public string InitialImage
{
get;
set;
}
private new ImageSource Source
{
get {return base.Source;}
set {base.Source = value;}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Loading the specified image
this.loadedImage = new BitmapImage();
this.loadedImage.BeginInit();
this.loadedImage.CacheOption = BitmapCacheOption.OnDemand;
this.loadedImage.DownloadCompleted += this.OnDownloadCompleted;
this.loadedImage.DownloadFailed += this.OnDownloadFailed;
this.loadedImage.UriSource = this.ImageUri;
this.loadedImage.EndInit();
// The image may be cached, in which case we will not use the initial image
if (!this.loadedImage.IsDownloading)
{
this.Source = this.loadedImage;
}
else
{
// Create InitialImage source if path is specified
if (!string.IsNullOrWhiteSpace(this.InitialImage))
{
BitmapImage initialImage = new BitmapImage();
// Load the initial bitmap from the local resource
initialImage.BeginInit();
initialImage.UriSource = new Uri(this.InitialImage, UriKind.Relative);
initialImage.DecodePixelWidth = (int)this.Width;
initialImage.EndInit();
// Set the initial image as the image source
this.Source = initialImage;
}
}
e.Handled = true;
}
private void OnDownloadFailed(object sender, ExceptionEventArgs e)
{
if (!string.IsNullOrWhiteSpace(this.LoadFailedImage))
{
BitmapImage failedImage = new BitmapImage();
// Load the initial bitmap from the local resource
failedImage.BeginInit();
failedImage.UriSource = new Uri(this.LoadFailedImage, UriKind.Relative);
failedImage.DecodePixelWidth = (int)this.Width;
failedImage.EndInit();
this.Source = failedImage;
}
}
private void OnDownloadCompleted(object sender, EventArgs e)
{
this.Source = this.loadedImage;
}
}
When i run the project a runtime error ocured:
Error: Property 'UriSource' or property 'StreamSource' must be set.
because this.ImageUri is null , i don't know why this.ImageUri be null ! help me
If it isn't the semicolon typo in InitialImage="/MyProject;component/Images/nopic.png",
maybe it's better to set your InitialImage as Default in ImageUri
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(new Uri("/MyProject/component/Images/nopic.png"), null));
UPDATE:
You have to bind to Image.Source and you could use PriorityBinding to show a placeholder.
<Image.Source>
<PriorityBinding>
<!--highest priority sources are first in the list-->
<Binding Path="YourImageUri"
IsAsync="True" />
<Binding Path="InitialImageUri"
IsAsync="True" />
</PriorityBinding>
</Image.Source>
For a "LoadFailedImage" a would subsribe to Image.ImageFailed Event.
Hope this helps.

Binding WP7 Maps control to ViewModel, Problem with MapMode

I am trying to reproduce the BingMaps sample of the Windows Phone 7 trainingkit:
http://msdn.microsoft.com/en-us/wp7trainingcourse_usingbingmapslab_topic2.aspx#_Toc271039352
but instead of wiring everything in codebehind i'd like to use a viewmodel.
Everything works fine except binding to the Mode property (aerial or road) causes a XamlParseException.
Is there a problem because it isn't a simple property?
This is the original Xaml:
<my:Map Name="Map"
CredentialsProvider="{Binding CredentialsProvider}">
<my:Map.Mode>
<my:AerialMode ShouldDisplayLabels="True" />
</my:Map.Mode>
</my:Map>
The Map.Mode can be changed from codebehind.
Instead I am trying the following:
<my:Map x:Name="Map"
CredentialsProvider="{Binding CredentialsProvider}"
ZoomLevel="{Binding Zoom, Mode=TwoWay}"
Center="{Binding Center, Mode=TwoWay}"
Mode="{Binding MapMode}" />
and the important part of the viewmodel:
private MapMode _mapMode = new AerialMode(true);
public MapMode MapMode
{
get { return _mapMode; }
set
{
_mapMode = value;
RaisePropertyChanged("MapMode");
}
}
private void ChangeMapMode()
{
if (MapMode is AerialMode)
{
MapMode = new RoadMode();
}
else
{
MapMode = new AerialMode(true);
}
}
Thanks for your help!
Solved.
"Mode" isn't a dependency property. So it cannot be bound.
My workaround:
added dependency property to view (=Page)
bound dependency property to property in viewmodel (via code in the constructor)
Set Mode of Map control in the propertyChanged callback handler
//Constructor
public MainPage()
{
InitializeComponent();
DataContext = new MainViewModel();
Binding b = new Binding("MapMode");
this.SetBinding(MapModeProperty, b);
}
//DependencyProperty. No need for corresponding CLR-property.
public static readonly DependencyProperty MapModeProperty =
DependencyProperty.Register("MapMode", typeof(MapMode), typeof(MainPage),
new PropertyMetadata(OnMapModeChanged));
//Callback
private static void OnMapModeChanged(DependencyObject element,
DependencyPropertyChangedEventArgs e)
{
((MainPage)element).Map.Mode = e.NewValue as MapMode;
}
Hope this one will help others!
I suspect you'll need to use a converter with your binding.

Resources