I fill a TreeView (WPF) in the code and want to use some icons in the items.
I can load and add BitmapImage but it is only displayed, when the BitmapImage is also assigned to another ImageControl in the window (and shown there):
TreeViewItem newItem = new TreeViewItem();
Image tempImage = new Image();
BitmapImage bitmapImage = new BitmapImage(new Uri(#"/Resources/ok-01.png", UriKind.Relative));
tempImage.Source = bitmapImage;
imageControlInWindow.Source = bitmapImage; //if I delete this line (it is not needed) the image in the TreeViewItem is not shown
TextBlock tempTextBlock = new TextBlock();
tempTextBlock.Inlines.Add(tempImage);
tempTextBlock.Inlines.Add("SomeText");
newItem.Header = tempTextBlock;
How can I force the image to be shown in the TreeView without the hack of showing it outside the treeview as copy?
You are not loading the image files from a proper Resource File Pack URI.
It should look like this:
var bitmapImage = new BitmapImage(new Uri("pack://application:,,,/Resources/ok-01.png"));
The Build Action of the image file must be set to Resource.
MainWindow:
<Window x:Class="TreeViewTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Text}" />
<Image Source="{Binding ImageSource}" Stretch="Uniform" Height="30"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
Code behind:
public partial class MainWindow
{
public List<MyItem> Items { get; private set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
Items = new List<MyItem>
{
new MyItem {Text = "Item 1", ImageSource = "/image1.png"},
new MyItem {Text = "Item 2", ImageSource = "/image2.png"}
};
}
}
public class MyItem
{
public string Text { get; set; }
public string ImageSource { get; set; }
}
Related
I am working on a WPF application where I want to use TabControl with ItemTemplate and ContentTemplate. I have a textblock in the ItemTemplate (for Header) and another textblock in the ContentTemplate(for Content).
I am binding a list to TabControl in constructor from code behind.
In Window loaded event, I want to loop over all tab items, access textblock in content template (Name="txt1") for each tab and set different text to textblock in each tab.
Below is my XAML:
<TabControl x:Name="mainTabs">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Name="txt1"></TextBlock>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Code behind:
public MainWindow()
{
InitializeComponent();
// Read tabs data from xml file
List<MyTab> lst = ReadTabsFromXml();
mainTabs.ItemsSource = lst;
}
public class MyTab
{
public string Header { get;set; }
public string Content { get; set; }
}
Please help me with this.
You don't need to do that. Just store your MyTab classes in an ObservableCollection<MyTab> instead of a List<MyTab>, and set the ItemsSource to the collection. The TabControl will update automatically.
EDIT: I've updated the answer, with a solution regarding using Gecko browser. You can wrap the Gecko browser in a UserControl that you use in each TabItem. Also note that a TabControl recreates the the TabItem every time you switch tabs, which also causes the webpage to reload. You can get around this by using e.g. TabControEx instead of TabControl (https://stackoverflow.com/a/9802346/6839733). But this is not related to the Gecko browser or to binding URL's from a collection - it's just how the TabControl works.
MainWindow.xaml:
<TabControl x:Name="MyTabControl">
<TabControl.Resources>
<DataTemplate x:Key="MyTabContentTemplate" x:Shared="False">
<local:GeckoBrowser Uri="{Binding Path=Uri}" />
</DataTemplate>
<Style TargetType="{x:Type TabItem}">
<Setter Property="ContentTemplate" Value="{StaticResource MyTabContentTemplate}" />
</Style>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public ObservableCollection<WebPageInfo> WebPageCollection { get; set; }
public MainWindow()
{
InitializeComponent();
// Read tabs data from xml file
//lst = ReadTabsFromXml();
WebPageCollection = new ObservableCollection<WebPageInfo>();
WebPageCollection.Add(new WebPageInfo() { Header = "Tab1", Uri = "https://www.amazon.com/" });
WebPageCollection.Add(new WebPageInfo() { Header = "Tab2", Uri = "https://www.cnn.com/" });
WebPageCollection.Add(new WebPageInfo() { Header = "Tab3", Uri = "https://www.microsoft.com/" });
WebPageCollection.Add(new WebPageInfo() { Header = "Tab4", Uri = "https://www.facebook.com/" });
MyTabControl.ItemsSource = WebPageCollection;
MyTabControl.SelectedIndex = 0;
}
public class WebPageInfo
{
public string Header { get; set; }
public string Uri { get; set; }
}
}
GeckoBrowser.xaml:
<UserControl x:Class="WpfApplication1.GeckoBrowser"
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"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border x:Name="MyBorder" Background="Black" Margin="0" />
</UserControl>
GeckoBrowser.xaml.cs:
public partial class GeckoBrowser : UserControl
{
public static readonly DependencyProperty UriProperty =
DependencyProperty.Register("Uri", typeof(string), typeof(GeckoBrowser), new FrameworkPropertyMetadata(null));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public GeckoBrowser()
{
InitializeComponent();
Xpcom.Initialize("Firefox");
Loaded += GeckoBrowser_Loaded;
}
private void GeckoBrowser_Loaded(object sender, RoutedEventArgs e)
{
WindowsFormsHost host = new WindowsFormsHost();
GeckoWebBrowser browser = new GeckoWebBrowser();
browser.Navigate(Uri);
host.Child = browser;
MyBorder.Child = host;
}
}
i am having an issue while trying to bind an image to an ImageSource. I have tried some of the other fix on stackoverflow but none of them works.
I seem to get an error on this line saying that collection "Items" must be empty.
ImageList.ItemsSource = List;
The bind works well while using the "url" member of the FlickrData class.
MainWindow.xaml
<ScrollViewer>
<ListView x:Name="ImageList" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<Rectangle Margin="5" Width="100" Height="100">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding imageBinding}"/>
</Rectangle.Fill>
</Rectangle>
</ListView>
</ScrollViewer>
FlickrData Class:
public class FlickrData
{
public String url { get; set;}
public FlickrData(Photo photo)
{
url = photo.SmallUrl;
}
public ImageBrush imageBinding
{
get
{
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(url));
return brush;
}
}
}
MainWindow class:
public partial class MainWindow : Window
{
public ObservableCollection<FlickrData> List = new ObservableCollection<FlickrData>();
public static Flickr flickr = new Flickr("XXXXXXXXXXXXXX");
public MainWindow()
{
InitializeComponent();
}
public void SearchWithInput(object sender, RoutedEventArgs e)
{
var options = new PhotoSearchOptions { Tags = SearchInput.Text, PerPage = 20, Page = 1 };
PhotoCollection photos = flickr.PhotosSearch(options);
List.Clear();
foreach (Photo photo in photos)
{
String flickrUrl = photo.WebUrl;
Console.WriteLine("Photo {0} has title {1} with url {2}", photo.PhotoId, photo.Title, photo.WebUrl);
List.Add(new FlickrData(photo));
}
ImageList.ItemsSource = List;
}
}
Do this to clean up the process
Change your XAML's ListView to ItemsSource="{Binding List}" which only needs to be done once.
Remove the now redundant ImageList.ItemsSource = List;
The list control will update itself accordingly because an ObservableCollection sends notifications of the change which are subscribed to by the list control.
I've got a GridviewItem. This GridviewItem has a background which is an ImageBrush. Now I want to change this ImageBrush to a new source when clicking a certain button.
For this I'm using:
blck.Background = new ImageBrush(new BitmapImage(new Uri("ms-appx:///Assets/SensorBG.png")));
It does work however the new image only shows whenever I click on the corresponding GridviewItem. Can anyone tell me how to update it without having to click on the GridviewItem?
I've already tried to put it within this block with no success:
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
blck.Background = new ImageBrush(new BitmapImage(new Uri("ms-appx:///Assets/SensorBG.png")));
}
);
The best would be, if you have defined your ItemClass with suitable properties and implement INotifyPropertyChanged - with appropriate binding, every change will update the UI. Here is a small sample - XAML:
<StackPanel>
<Button Content="Change background of second item" Click="Button_Click"/>
<GridView Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" ItemsSource="{x:Bind Items}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:ItemClass">
<Border>
<Border.Background>
<ImageBrush ImageSource="{x:Bind Image, Mode=OneWay}"/>
</Border.Background>
<TextBlock Text="{x:Bind Name}"/>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</StackPanel>
and the code behind:
public sealed partial class MainPage : Page
{
public List<ItemClass> Items = new List<ItemClass>();
public MainPage()
{
Items.Add(new ItemClass { Name = "First item", Image = new BitmapImage(new Uri("ms-appx:///Assets/StoreLogo.png")) });
Items.Add(new ItemClass { Name = "Second item", Image = new BitmapImage(new Uri("ms-appx:///Assets/StoreLogo.png")) });
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e) => Items[1].Image = new BitmapImage(new Uri("ms-appx:///test.jpg"));
}
public class ItemClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private ImageSource image;
public ImageSource Image
{
get { return image; }
set { image = value; RaiseProperty(nameof(Image)); }
}
public string Name { get; set; }
}
When I declare a TreeView in the XAML, I can use a control of my choice (here, a StackPanel) for the elements that are immediately added to it:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<DockPanel Name="dockPanel1">
<TreeView Name="treeView1">
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ProgressBar Height="15" Width="160" />
<TextBlock Foreground="Red" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
<sys:String>Foo</sys:String>
<sys:String>Bar</sys:String>
</TreeView>
</DockPanel>
</Window>
How can I achieve the same thing when adding elements from the C# code?
namespace WpfApplication5
{
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
// I want something more complex than just "Quux".
var item = new TreeViewItem { Header = "Quux" };
treeView1.Items.Add(item);
}
}
}
When I declare a TreeView in the XAML, I can use a control of my choice (here, a StackPanel) for the elements that are immediately added to it
That goes for all items, in code just do:
treeView1.Items.Add("Text");
Or
treeView1.ItemsSource = new[]
{
"One", "Two"
};
Unless you add UI elements the defined DataTemplate will be used.
Might want to read some references...
Just do it :) .
namespace WpfApplication5
{
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
var stackPanel = new StackPanel { Orientation = Orientation.Horizontal };
stackPanel.Children.Add(new ProgressBar { Height = 15, Width = 160 });
stackPanel.Children.Add(new TextBlock { Foreground = new SolidColorBrush(Colors.Red), Text = "Quux" });
var item = new TreeViewItem { Header = stackPanel };
treeView1.Items.Add(item);
}
}
}
I'm trying to view icons and text as a table, so the code looks like
MyItemType.cs
public class MyItemType
{
public byte[] Image { get; set; }
public string Title { get; set; }
}
MainWindow.cs
public MainWindow()
{
InitializeComponent();
MemoryStream mstream = new MemoryStream();
Bitmap b = new Bitmap(#"C:\Users\Eliazar\Pictures\1556.bmp");
b.Save(mstream, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] ba = mstream.ToArray();
BinaryWriter writer = new BinaryWriter(mstream);
writer.Write(ba);
MyItems = new List<MyItemType>();
MyItemType newItem = new MyItemType();
newItem.Image = ba;
newItem.Title = "FooBar Icon";
MyItems.Add(newItem);
this.MainGrid.DataContext = this;
}
public List<MyItemType> MyItems { get; set; }
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyItemType}">
<StackPanel>
<Image Source="{Binding Path=Image}"/>
<TextBlock Text="{Binding Path=Title}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid Name="MainGrid">
<ListBox ItemsSource="{Binding Path=MyItems}" Background="White" Width="400" HorizontalAlignment="Right" Margin="0,211.206,35,188.794">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
But nothing appears in the window. Does anybody have an idea of what's wrong?
Image.Source needs to be of type ImageSource (MSDN link). It does not know how to handle an array of bytes, as far as I know.
agreed with Jens, Image.Source has to be of Type ImageSource (or BitmapImage)
You should do something like this:
string path = #"C:\Users\Eliazar\Pictures\1556.bmp";
BitmapImage source = new BitmapImage();
source.BeginInit();
source.UriSource = new Uri(path, UriKind.Absolute);
source.EndInit();
newItem.Image = new Image() { Source = source };