I want to create a Windows Toolbar (something like RocketDock) which dynamically creates buttons from serialized classes, but I can't figure out how to dynamically set the image.
As an aside, how should a image be serialized (i want to serialize the image with the storage class, instead of loading it from the original png file location each time)?
I found the following code to create a dependency property
public class ImageButton
{
#region Image dependency property
public static readonly DependencyProperty ImageProperty;
public static ImageSource GetImage(DependencyObject obj)
{
return (ImageSource)obj.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject obj, ImageSource value)
{
obj.SetValue(ImageProperty, value);
}
#endregion
static ImageButton()
{
var metadata = new FrameworkPropertyMetadata((ImageSource)null);
ImageProperty = DependencyProperty.RegisterAttached("Image",
typeof(ImageSource),
typeof(ImageButton), metadata);
}
}
And i created a button style to inherit to set the image
<Style x:Key="ImageButton" TargetType="{x:Type Button}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Image Source="{Binding Path=(Extender:ImageButton.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"
HorizontalAlignment="Left" Margin="8,0,0,0" Height="16" Width="16" />
<TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
but I can't figure out how to set the image from code after I create the button
var newButton = new Button();
var style = (Style)FindResource("ImageButton");
newButton.Image does not resolve. Do I need to do something with style.Resources?
EDIT - response to Olli
Thanks, but... Using...
Link link = new Link(#"c:\temp\dev\ClearAllIcon.png");
var newButton = new Button();
newButton.Content = "bloberific";
var style = (Style)FindResource("ImageButton");
ImageButton.SetImage(newButton, link.IconSource);
MainStack.Children.Add(newButton);
where link is defined as
public Link(string iconPath)
{
IconPath = iconPath;
IconSource = new BitmapImage(new Uri(iconPath));
}
the image does not show, though the text does. The button is normal looking.
You did not create a normal dependency property but an attached dependency property. Your newButton instance has no defined property "Image". Therfore you do not have the convenience CLR property getter/setter (see MSDN for details).
The code should look similar to this to set the image source value:
ImageButton.SetImage(newButton, imageSource);
Link link = new Link(#"c:\temp\dev\ClearAllIcon.png");
var newButton = new Button();
newButton.Content = "bloberific";
var style = (Style)FindResource("ImageButton");
ImageButton.SetImage(newButton, link.IconSource);
MainStack.Children.Add(newButton);
you do not use the style variable - is this the problem ?
This worked for me some time ago to create a little game.
var botao = sender as Button;
Image i = new Image();
i.Source= new BitmapImage(new Uri(#"/Resources/x.png", UriKind.RelativeOrAbsolute));
botao.Content = i;
Related
I have a TabControl with some tabs declared in XAML. I want to add new tabs and bind their IsEnabled properties to some properties of their content:
for (int i = 0; i < context.Pictures.Count; ++i)
{
var tabItem = new TabItem();
var title = "Some title"
tabItem.Header = title;
var image = new Image();
Binding sourceBinding = new Binding(nameof(context.Pictures) + $"[{i}]");
sourceBinding.Source = context;
image.SetBinding(Image.SourceProperty, sourceBinding);
image.Width = 800;
image.Height = 600;
DataTrigger isEnabledTrigger = new DataTrigger() { Binding = sourceBinding, Value = null };
isEnabledTrigger.Setters.Add(new Setter(TabItem.IsEnabledProperty, false));
tabItem.Content = image;
tabControl.Items.Add(tabItem);
}
I want to disable tab if the picture inside is null (apply isEnabledTrigger). Problem here is that style of tabItem is derived from tabControl containing it, so I cannot just create a style with my trigger and apply it to TabItem. Sure, I could just copy original style and hardcode it, but I don't think it's a good way to solve my problem.
So, to solve my problem I have two ideas:
Create a shallow copy of existing style, add trigger and apply it
Load original style from XAML, add trigger and apply it (may be difficult, since it lies in another project)
Is there more rational way to bind TabControls IsEnabled to contained Images value?
Don't add TabItem directly. Use data models. This is recommended approach for all item controls. Then define a DataTemplate for the data model(s) and assign it to TabControl.ContentTemplate.
Use the TabControl.ItemTemplate to layout the header.
Defining a Style for the TabControl.ItemContainerStyle allows you to set up the required triggers quite easily. Doing layout using C# is never a good idea. Always use XAML.
See: Data binding overview in WPF, Data Templating Overview
The minimal model class should look like this:
PictureModel.cs
// All binding source models must implement INotifyPropertyChanged
public class PictureModel : INotifyPropertyChanged
{
private string title;
public string Title
{
get => this.title;
set
{
this.title = value;
OnPropertyChanged();
}
}
private string source;
public string Source
{
get => this.source;
set
{
this.source = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<PictureModel> Pictures { get; }
private void CreateTabItems(Context context)
{
foreach (string imageSource in context.Pictures)
{
var pictureModel = new PictureModel()
{
Title = "Some Title",
Source = imageSource
};
this.Pictures.Add(pictureModel);
}
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<!-- Layout the tab content -->
<TabControl ItemsSource="{Binding Pictures}">
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type viewModels:PictureModel}">
<Image Source="{Binding Source}" />
</DataTemplate>
</TabControl.ContentTemplate>
<!-- Layout the tab header -->
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:PictureModel}">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</TabControl.ItemTemplate>
<!-- Setup triggers. The DataContext is the current data model -->
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Source}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</Window>
You should base your Style on the current Style:
Style style = new Style(typeof(TabItem))
{
BasedOn = FindResource(typeof(TabItem)) as Style
};
DataTrigger isEnabledTrigger = new DataTrigger() { Binding = sourceBinding, Value = null };
isEnabledTrigger.Setters.Add(new Setter(TabItem.IsEnabledProperty, false));
style.Triggers.Add(isEnabledTrigger);
tabItem.Style = style;
Or
Style style = new Style(typeof(TabItem))
{
BasedOn = tabControl.ItemContainerStyle
};
...
...depending on how your current Style is applied.
This is how you would extend an existing Style with your DataTrigger and this is a good way of solving this.
i am using wpf tab control and setting the Icon and text in the tab header through style, i am able to set the text dynamically through getter and setter but can not able to set the image source. I tried to bind the image source through getter and setter but failed. Following is the style code in which i want to set the image source dynamically from the code behind.
-->
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--<Image gif:ImageBehavior.AnimatedSource="{DynamicResource MyFillBrush}" Width="20" />-->
<Image gif:ImageBehavior.AnimatedSource="25.gif" Width="20" />
<Label Content="{Binding }"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
I know this is a bit old thread, but this is how you update the AnimatedSource from code:
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(value);
image.EndInit();
ImageBehavior.SetAnimatedSource(Imagereference, image);
The 'value' is the location of your gif file such as "25.gif".
The Imagereference is the Image control you want to update. Also ensure you have the namespace referenced: using WpfAnimatedGif;
Maybe you can use Binding, something like
private string _dynamicGif = "test.gif";
public string DynamicGif
{
get { return _dynamicGif; }
private set
{
_dynamicGif = value;
RaisePropertyChanged("DynamicGif");
}
}
and
<Image gif:ImageBehavior.AnimatedSource="{Binding DynamicGif, UpdateSourceTrigger=PropertyChanged}" Width="20" />
Or, if it doesn't work, you can use MediaElement instead of Image, it won't require WPF Animated GIF. It's also pretty simple to use:
private Uri _imgSource = new Uri("test.gif");
public Uri ImgSource
{
get { return _imgSource; }
private set
{
_imgSource = value;
RaisePropertyChanged("ImgSource");
}
}
and
<MediaElement x:Name="gifImg" LoadedBehavior="Play" Width="20" Source="{Binding ImgSource}"/>
Hope this helps.
I've got a class, which holds 8 BitmapImage in an array. The Images are added in the following way in the class constructor:
public SymbolCollection(string[] symbolNames)
{
Symbols = new BitmapImage[8];
for (int i = 0; i < Symbols.Length; i++)
{
Symbols[i] = new BitmapImage();
Symbols[i].BeginInit();
Symbols[i].UriSource = new Uri(#"/Resources/" + symbolNames[i] + ".png", UriKind.Relative);
Symbols[i].EndInit();
}
}
The button's ImageSource are set to the following:
<Button x:Name="buttonPlayer1" HorizontalAlignment="Left" Height="80" Margin="44,387,0,0" VerticalAlignment="Top" Width="80" Click="OnPlayerSymbolClick">
<Button.Template>
<ControlTemplate>
<Image Source="{Binding Content}"/>
</ControlTemplate>
</Button.Template>
</Button>
I tried to set the image to the button in the MainWindow() method and change it in the OnPlayerSymbolClick() method, but the image doesn't even show up:
public MainWindow()
{
this.InitializeComponent();
SetSymbolCollection();
Uri u = symbolCollectionHolder[0].Symbols[0].UriSource;
Debug.WriteLine(u.ToString());
Image i = new Image();
BitmapImage bmp = new BitmapImage();
bmp.UriSource = u;
i.Source = bmp;
buttonPlayer1.Content = i;
}
private void OnPlayerSymbolClick(object sender, RoutedEventArgs e)
{
ImageBrush imagebrush = new ImageBrush();
imagebrush.ImageSource = symbolCollectionHolder[0].Symbols[1];
buttonPlayer1.Background = imagebrush;
buttonPlayer1.Content = imagebrush;
Debug.WriteLine(buttonPlayer1.Background.ToString());
Debug.WriteLine(imagebrush.ImageSource.ToString());
}
The Build Action for the png-s are set to Resource and there's a Resources folder in the project.
When an image resource file is to be loaded into a BitmapImage in code behind, you should use a Resource File Pack URI:
Symbols[i] = new BitmapImage(new Uri(
"pack://application:,,,/Resources/" + symbolNames[i] + ".png"));
Besides that, setting the Content or Background property is pointless when the Button's Template doesn't use these properties.
You probably wanted to write the Template like this:
<Button x:Name="buttonPlayer1" ...>
<Button.Template>
<ControlTemplate TargetType="Button">
<Image Source="{TemplateBinding Content}"/>
</ControlTemplate>
</Button.Template>
</Button>
which would require an ImageSource (or a derived object) as value of the Button's Content.
You could directly pass one of your Symbols:
buttonPlayer1.Content = Symbols[0];
I have a few button created from a list, I now need to change one of the buttons image.
i am now trying to convert the string to a button.
How do i take the string and turn it in to a button that i can then work with ?
i have tried this:
Button button = (Button)this.FindName("button_" + list[0].ToString());
button.background = brush;
i get an error of Object reference not set to an instance of an object when i call button.background.
EDIT
this is how i have chosen to change the image of my button:
Uri resourceUri = new Uri("led_On.png", UriKind.Relative);
StreamResourceInfo streamInfo = Application.GetResourceStream(resourceUri);
BitmapFrame temp = BitmapFrame.Create(streamInfo.Stream);
var brush = new ImageBrush();
brush.ImageSource = temp;
Button button = (Button)FindName("button_" + list[0].ToString());
button.Background = brush;
i just cant grab the button with the name button_list[0].
One possible solution to change the WPF Button Image on Click event (C#) is listed below:
Listing 1. Changing Button Image on click (XAML)
<Button Name ="button1" Click="button1_Click">
<Button.Template>
<ControlTemplate TargetType="Button">
<Image x:Name="image1">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="Images/ContentImage.png" />
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Button.Template>
</Button>
Listing 2. C# code behind (button1.click event handler)
private void button1_Click(object sender, RoutedEventArgs a)
{
Image _img= button1.Template.FindName("image1", button1) as Image;
Style _imgStyle = new Style { TargetType = typeof(Image) };
_imgStyle.Setters.Add(new Setter(Image.SourceProperty, new BitmapImage(new Uri(#"pack://application:,,,/YourAssemblyName;component//Images/ContentImage1.png"))));
_img.Style = _imgStyle;
}
Pertinent to your code, it could be re-written in a simple form:
StreamResourceInfo _streamInfo =
Application.GetResourceStream(new Uri("led_On.png", UriKind.Relative));
button.Background = (new ImageBrush(){ImageSource=BitmapFrame.Create(_streamInfo.Stream)});
Just make sure that button object is not null (check this statement: "button_" + list[0] - may be just list[0]?)
Hope this may help.
I want to change image dynamically on a button. I think I have what is required to find the image. I have som resourcedictionary for the buttons here:
<BitmapImage x:Key="Mark1Empty" UriSource="Marks/Tom1.png"/>
<BitmapImage x:Key="Mark1Chosen" UriSource="Marks/Ny1.png"/>
Then afterwards, I create the button like this in XAML User control:
<Button x:Name="Mark1Button"
Height="30" Width="40" Template="{StaticResource Mark1ButtonTemplate}"
cal:Message.Attach="[Event Click] = [Action SayHello($source, $this)]"/>
The ControlTemplate looks like this:
<ControlTemplate x:Key="Mark1ButtonTemplate" TargetType="{x:Type Button}">
<Image Name="Mark1ButtonImage" Source="{StaticResource Mark1Empty}" />
</ControlTemplate>
After that. In C# code when I press the image. Then it triggers, but the image doesn't change. It is blank after clicking on the image.
public void SayHello(object sender, object kravsource)
{
var selectedButton = sender as Button;
if (selectedButton != null)
{
Image image = (Image)selectedButton.Template.FindName("Mark1ButtonImage", selectedButton);
BitmapImage imagePicker = (BitmapImage)Application.Current.FindResource("Mark1Chosen");
image.Source = new BitmapImage(new Uri(imagePicker.UriSource.ToString(), UriKind.RelativeOrAbsolute));
image.Stretch = Stretch.Uniform;
}
}