This question already has an answer here:
How do I change an image source dynamically from code-behind in WPF with an image in Properties.Resources?
(1 answer)
Closed 1 year ago.
XAML code:
<ListView x:Name="Toolbar" Grid.Row="1" ItemsSource="{Binding List}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel MouseLeftButtonDown="Lamp_Click" Name="Lamp" Background="White" Width="34" Height="35">
<Grid Height="30">
<Image Source="images/{Binding Path}">
</Image>
<TextBlock Text="{Binding Name}" FontSize="7" TextAlignment="Center" Margin="-1,19,1,-12"></TextBlock>
</Grid>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C# code:
public class MyClass
{
public static List<ViewModel> List{ get; set; } = GetAll();
public static List<ViewModel> GetAll()
{
// get records from data base.
}
}
This code has syntax error. How can I change images/{Binding path} to a valid. why this code has error? I'm really confused. Please help me.
<Image Source="images/{Binding Path}">
XAML doesn't support this kind of half binding thing you're doing, you either bind something or you don't. And even if it did, it wouldn't work like that because Image.Source isn't a string, it's an ImageSource.
Instead you need to create a converter that converts your Path binding (a string) to an ImageSource by instantiating a BitmapImage and setting its source to load the image asynchronously.
Related
I have some WPF control called Foo. Grid structure with DevExpress LoadingDecorator looks like that:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<dx:LoadingDecorator Name="Decorator" IsSplashScreenShown="{Binding Path=ShowLoader}" SplashScreenLocation="CenterWindow">
<dx:LoadingDecorator.SplashScreenTemplate>
<DataTemplate>
<dx:WaitIndicator DeferedVisibility="True">
<dx:WaitIndicator.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="Operation:" FontSize="15"/>
<TextBlock Text="{Binding Path=CurrLoadStat}"/>
</StackPanel>
</DataTemplate>
</dx:WaitIndicator.ContentTemplate>
</dx:WaitIndicator>
</DataTemplate>
</dx:LoadingDecorator.SplashScreenTemplate>
</dx:LoadingDecorator>
</StackPanel>
...
</Grid>
Base ViewModel class implements INotifyPropertyChanged interface and ViewModel class (FooViewModel) used as DataContext for control with Grid described above inherits from it. I have implemented property to change text property in 2nd TextBlock element:
private string currLoadStat = "Preparing data...";
public string CurrtLoadStat
{
get
{
return currLoadStat;
}
set
{
currLoadStat = value;
OnPropertyChanged("CurrLoadStat");
}
}
My problem is that binding instruction doesn't work and I see only text defined in first TextBlock.
Can You provide me some solution to resolve this problem?
Your property has a "t" in the middle of the name but your binding path (XAML) and magic string you pass to OnPropertyChanged do not. The string you pass when you raise the PropertyChanged event method must exactly match your view model's property name.
If you are using C# 5 or 6 then switch to using one of the approaches outlined here and you would eliminate the need for passing magic strings.
I have a Listbox done like this
<ListBox x:Name="lbAlbumSelect">
<ListBox.ItemTemplate>
<DataTemplate>
<Button>
<Button.Content>
<StackPanel>
<Image />
<TextBlock TextWrapping="Wrap"
Text="{Binding album_name}" />
</StackPanel>
</Button.Content>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want to access every Image programmatically and set its Source. I tried to navigate the listbox like this
foreach (Button btn in lbAlbumSelect.Items)
{
StackPanel stack=btn.Content;
Image image=stack.Children.ElementAt(0) as Image;
//every ListBoxItem is binded to a clsAlbums object that contains various data,
//also the name of the image file, but not the path.
string pathImg = #"/Assets/Images/" + (btn.DataContext as clsAlbums).album_img;
LoadImage(pathImg, image); //function that sets image source to path img
}
But gives me a Invalid Cast Exception on the foreach clause.
Is there a faster and more correct way to do this?
You should ideally be binding the image source to the control. Add an additional property to your class clsAlbums which can be bound to the Image source.
public class clsAlbum
{
public string album_name { get; set; }
public string album_img { get; set; }
public string album_img_src
{
get
{
return #"/Assets/Images/" + album_img;
}
}
}
Now bind this additional property album_img_src to your xaml.
<ListBox x:Name="lbAlbumSelect">
<ListBox.ItemTemplate>
<DataTemplate>
<Button>
<Button.Content>
<StackPanel>
<Image Source="{Binding album_img_src}"/>
<TextBlock TextWrapping="Wrap"
Text="{Binding album_name}" />
</StackPanel>
</Button.Content>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The item is actually ListBoxItem. In your case Button is content of ListBoxItem and Content of Button is StackPanel and Image is child of StackPanel. So you need to traverse visual tree somehow and you can do so using Linq to visual tree, for example. http://www.codeproject.com/Articles/63157/LINQ-to-Visual-Tree
Probably easiest way of accessing elements inside datatemplate is from it's loaded or initialized event:
here:
<Image Loaded="Image_Loaded" />
void Image_Loaded(object sender, EventArgs e){
var image =(Image)sender;
Try to avoid acessing elements inside datatemplates. 90% times you achieve your goal better, using ViewModel, converters, using behaviours, datatriggers or extracting datatemplate to separate UserControl
OK, here's my issue :
I've got a custom class, with a list of items - each of which has an image path associated with it
I've added a folder WITH the images inside the project (so I suppose there are being added in the XAP too, huh?)
When I'm trying to bind the Source of an image item in XAML, it's not working.
<ListBox Margin="0,0,-12,0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,17">
<Image Height="100" Width="100" Margin="12,0,9,0" Source="/AlbumArt/{Binding AlbumArt}"/>
<StackPanel Width="311">
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}"/>
<TextBlock Text="{Binding Author}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What am I doing wrong? Any ideas?
P.S.
I've also tried Source="{Binding AlbumArt}" but it's still not displaying anything.
The craziest thing is that, if I set the Source to a specific image (e.g. /AlbumArt/someImage.jpg), the image seem to work fine in Visual Studio & the Emulator.
I don't think Binding works that way. If it is not possible to prepend the string "/AlbumArt/" to your image path property (which is AlbumArt), I would suggest using a converter to do so.
Also, formatting strings only works when the target property is a string so StringFormat is out. Someone correct me if I'm wrong about StringFormat
If you have binding issues always check the output window for details. That is where information about binding errors is displayed.
This Source="/AlbumArt/{Binding AlbumArt}" won't work because it will just be treated as a string.
Without seeing your class it's hard to be certain but the property you're binding to ("AlbumArt") should be a Uri and not a string and it should be populated with a relative Uri to the image. That image should also be set to having the build action of Content.
what you have to is this..
<Image Height="100" Width="100" Margin="12,0,9,0" Source="{Binding ImagePath}"/>
as you said you have list of items( i think it would be class that also having paths of images ) so make this property in this class and for every item put the path in this property for coresponding item.
private string _ImagePath;
public string ImagePath
{
get
{
return _ImagePath;
}
set
{
_imagePath = value;
}
}
it would be better if you implement INotifyPropertyChanged in your item class.
How about getting more nesty.
private string _AlbumArt;
public string AlbumArt
{
get
{
return _AlbumArt;
}
set
{
if(_AlbumArt!=null)
_AlbumArt=#"/AlbumArt/"+ value;
}
}
and Binding
<Image Height="100" Width="100" Margin="12,0,9,0" Source="{Binding AlbumArt}"/>
I'm pretty sure you've just used the wrong path, try this one....
"/ApplicationName;component/AlbumArt/{Binding AlbumArt}"
Replace the ApplicationName section with your applications name of course. MAKE SURE TO REPLACE SPACES IN IT WITH %20
I have an app for windows 8 that needs to take a Json string and deseriaizes it into DATACONTRACTS and it will display the information I wish in a Listbox that will have a max height and will scroll if greater than the max height.
The problem that im having it not so much as not being able to do it but rather not knowing how to do it.
So far I can deserialize the Json and I can specify where I want each item to go into the UI but what im trying to do is basically a for each item in the array I want it to make a new Stackpanel formatted with Textblocks that will have the information from the Json. I don't know how to this unfortunately and I don't really know what im searching for to get tutorials on how to do it
This is the code I have that takes the items from the json with a helper class and puts them in the Text of the TextBlocks.
var _FilterSaleList = new FilterSalesList();
var _Sales = await _FilterSaleList.FindSalesbyFilters();
string _SaleName = _Sales.sales[0].name.ToString();
string _SaleDescription = _Sales.sales[0].description.ToString();
string _SaleName1 = _Sales.sales[1].name.ToString();
string _SaleDescription1 = _Sales.sales[1].description.ToString();
int _TotalResults = _Sales.sales.Length;
SaleTitle.Text = _SaleName;
SaleDescription.Text = _SaleDescription;
SaleTitle1.Text = _SaleName1;
SaleDescription1.Text = _SaleDescription1;
This is the XAML code for the Listbox with 2 Stack panels already in it.
<ListBox Grid.Row="1">
<StackPanel Margin="0,0,0,5">
<TextBlock x:Name="SaleTitle" Text="" HorizontalAlignment="Center" Margin="0,0,0,5"/>
<TextBlock x:Name="SaleDescription" Text="" HorizontalAlignment="Center" MaxHeight="40" Margin="0,0,0,5" TextWrapping="Wrap"/>
</StackPanel>
<StackPanel Margin="0,0,0,5">
<TextBlock x:Name="SaleTitle1" Text="" HorizontalAlignment="Center" Margin="0,0,0,5"/>
<TextBlock x:Name="SaleDescription1" Text="" HorizontalAlignment="Center" MaxHeight="40" Margin="0,0,0,5" TextWrapping="Wrap"/>
</StackPanel>
</ListBox>
Below is an image of how I would like it to look.
even though everything works this way like I said I would like it so that each item from the json will make a new stackpanel and display the information as in the image. I don't know what its called when this is done so even a simple hint as to where to look would be great!
http://puu.sh/2biMZ
In XAML there is a very nice feature called Binding, which allows you to simply bind an object or a list of objects to visual element. This way, you don't have to "build" the graphical user interface manually in C# code.
This is a very large topic, so you should probably have a look at what is MVVM, it will help you leverage the power of Binding : http://channel9.msdn.com/Series/Building-Apps-for-Both-Windows-8-and-Windows-Phone-8-Jump-Start/Building-Apps-for-Both-Windows-8-and-Windows-Phone-8-03-Model-View-ViewModel
But for now, what you could is :
1/ Define your ListBox as following, with a DataTemplate for the ItemTemplate property :
<ListBox Grid.Row="1" x:Name="SalesListbox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,5">
<TextBlock x:Name="SaleTitle" Text="{Binding name}" HorizontalAlignment="Center" Margin="0,0,0,5"/>
<TextBlock x:Name="SaleDescription" Text="{Binding description}" HorizontalAlignment="Center" MaxHeight="40" Margin="0,0,0,5" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The DataTemplate will tell how each item of the list should be rendered. You should also notice how we used Binding for the Text properties in each textblock. It's bound to name and description which are the name of the properties in your model.
And then you can populate your ListBox with your data :
var filterSaleList = new FilterSalesList();
var salesByFilters = await filterSaleList.FindSalesbyFilters();
SalesListbox.ItemsSource = salesByFilters.sales;
we have an Array which is converted via a Binded Converter:
else if (TTools.IsOfBaseClass(value.GetType(), typeof(System.Activities.Presentation.Model.ModelItemCollection)))
{
OurBaseClass[] test = (value as ModelItemCollection).GetCurrentValue() as OurBaseClass[];
List<OurBaseClass> listOfArray = new List<OurBaseClass>();
foreach (OurBaseClass item in test)
{
listOfArray.Add(item);
}
return listOfArray;
}
the convertion works well but it is not shown in our dynamically gui
gui code with bindings:
<sap:WorkflowItemsPresenter xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation" Grid.Column="0" Name="MyArray" Items="{Binding Path=ModelItem.MyArray}" MinWidth="150" Margin="0">
<sap:WorkflowItemsPresenter.SpacerTemplate >
<DataTemplate>
<TextBlock Foreground="DarkGray" Margin="30">..</TextBlock>
</DataTemplate>
</sap:WorkflowItemsPresenter.SpacerTemplate>
<sap:WorkflowItemsPresenter.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="0"/>
</ItemsPanelTemplate>
</sap:WorkflowItemsPresenter.ItemsPanel>
</sap:WorkflowItemsPresenter>
Why is the gui not shown as a List??? it works well without converter.
Thanks
Have you tried setting a breakpoint in the converter?
I think the first problem may be that ModelItem.MyArray is type ModelProperty, rather than ModelItemCollection.