I am binding a list of urls to a ListBox (MVVM) and found that if the model is a string[] everything works fine but if it's a List<Uri> then no items are displayed in my ListBox. I assume this is because WPF doesn't know how to convert a Uri into a string but
I'd figure it would just call ToString() which is what I want
I don't know how to tell WPF how to do the right thing
Here's my XAML:
<ListBox Height="200" ItemsSource="{Binding Path=UrlsFound, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate DataType="String">
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now as long as UrlsFound is a string[] the binding works, but if I refactor to make it a List<Uri> nothing is displayed in the ListBox. I changed the DataType="String" to "Uri" but that didn't help
There must be something else wrong as I've copied your XAML and it works.
Here's my code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
urlsFound.Add(new Uri("http://www.google.com"));
urlsFound.Add(new Uri("http://www.google.com"));
urlsFound.Add(new Uri("http://www.google.com"));
this.DataContext = this;
}
private List<Uri> urlsFound=new List<Uri>();
public List<Uri> UrlsFound
{
get { return urlsFound; }
set { urlsFound = value; }
}
}
Related
I try to use binding to display Hi in the Text content.
However, when clicking the button, it doesn't work.
Could someone help me to solve the problem?
Thanks.
1.XAML CODE :
<Window x:Class="Wpftest.binding.Window0"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window0" Height="300" Width="300">
<Grid>
<TextBox x:Name="textBox2" VerticalAlignment="Top" Width="168"
Text="{Binding Source= stu, Path= Name, Mode=TwoWay}"/>
</Grid>
</Window>
2.Class :
namespace Wpftest.binding.Model
{
public class student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set { name = value;
if(this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new
PropertyChangedEventArgs("Name"));
}
}
}
}
}
3.XAML.cs:
namespace Wpftest.binding
{
public partial class Window0 : Window
{
student stu;
public Window0()
{
InitializeComponent();
stu = new student();
}
private void button_Click(object sender, RoutedEventArgs e)
{
stu.Name += "Hi!";
}
}
}
There are many ways to achieve what you need; the correct method depends very much on what style of application you want to create. I'll demonstrate two methods that will require minimal changes from your supplied example:
Method 1
Set the DataContext to stu and bind to the Name property.
XAML.cs
private student stu;
public Window0()
{
InitializeComponent();
stu = new student();
DataContext = stu;
}
XAML code
<TextBox Text="{Binding Path=Name, Mode=TwoWay}"/>
Method 2
Generally you will set the DataContext to some object other than the Window (e.g. the ViewModel if you are following the MVVM pattern), but sometimes you may need to bind a control to some property of the Window. In this case the DataContext can't be used, but you can still bind to a property of the Window by using RelativeSource. See below:
XAML.cs
// note this must be a property, not a field
public student stu { get; set; }
public Window0()
{
InitializeComponent();
stu = new student();
}
XAML code
<TextBox Text="{Binding Path=stu.Name, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
Hint: if you are having trouble with WPF data binding, then it often helps to look at the debugger output window to see the binding trace messages. And debugging can be further enhanced by adding this namespace to the Window element
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then setting the TraceLevel e.g.
<TextBox Text="{Binding Source=stu, diag:PresentationTraceSources.TraceLevel=High}"/>
Basically you need to set DataContext property to your Window.
For example:
public MainWindow()
{
DataContext=new YourViewModel();
}
DataContext of Window is a way to communicate between View(XAML) and ViewModel(C# code)
In addition, you can add DataContext in xaml:
<Window.DataContext>
<local:YourViewModel/>
</Window.DataContext>
Also, instead of handling Click event, you should use Command property of Button. Example can be seen here.
Is it the intended behavior that a binding to a collection automatically uses the first item as source?
Example Xaml:
<Window x:Class="ListSelection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBlock Text="{Binding ColContent}" />
<TextBlock Text="{Binding ItemContent}" />
</StackPanel>
</Window>
and Code:
using System.Collections.Generic;
using System.Windows;
namespace ListSelection
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyCol("col 1")
{
new MyItem("item 1"),
new MyItem("item 2")
};
}
}
public class MyItem
{
public string ItemContent { get; set; }
public MyItem(string content)
{
ItemContent = content;
}
}
public class MyCol : List<MyItem>
{
public string ColContent { get; set; }
public MyCol(string content)
{
ColContent = content;
}
}
}
The UI shows up with:
col 1
item 1
The second binding took implicitly the first collection item as source! So bug, feature or intended?
EDIT: .net 4.5, VS2012, corrections
EDIT 2:
I further investigated the problem together with a mate and got closer to the solution:
<Window x:Class="ListSelection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<ListView ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemContent}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Text="{Binding ItemContent}" />
</StackPanel>
</Window>
The - lets call it - magic binding seems to exist for master detail views. By default any collection that is bound gets a CollectionView - which provides a selected item property (and other cool stuff like sorting). This selected item can be used shortcutted for the detailed view. If the IsSynchronizedWithCurrentItem is set to true the shortcutted binding reacts to changed selections. The problem in the whole thing: the selected item of the CollectionView is alway set to the first item which leads to the magic binding... I would call that a bug and it should only work explicitly, e.g. by binding the collection to a Selector with the IsSynchronizedWithCurrentItem set.
I wonder how I can show design time data in Expression Blend that is located inside a SampleData.xaml using a CollectionViewSource? Before changing my code to use the CVS, I used an ObservableCollection. I was in the need to filter and sort the items inside there, thus I changed the code to use the CVS. Now my designer complains about not being able to fill the SampleData's NextItems with a proper structure to show up in Expression Blend. Here is some code I use inside the app:
MainViewModel.cs
class MainViewModel
{
public MainViewModel()
{
AllItems = new ObservableCollection<ItemViewModel>();
NextItems = new CollectionViewSource();
NextItems.Source = AllItems;
}
public CollectionViewSource NextItems
{
get;
private set;
}
public ObservableCollection<ItemViewModel> AllItems
{
get;
private set;
}
some functions to fill, filter, sort etc...
}
MainView.xaml:
<phone:PhoneApplicationPage
... some other stuff ...
d:DesignWidth="480"
d:DesignHeight="728"
d:DataContext="{d:DesignData SampleData/SampleData.xaml}">
<Grid
x:Name="LayoutRoot"
Background="Transparent">
<controls:Panorama>
<controls:PanoramaItem>
<ListBox ItemsSource="{Binding NextItems.View}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Image}" />
<StackPanel>
<TextBlock Text="{Binding FullName}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PanoramaItem>
</controls:Panorama>
</Grid>
</phone:PhoneApplicationPage>
SampleData.xaml
<local:MainViewModel
xmlns:local="clr-namespace:MyAppNamespace"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:swd="clr-namespace:System.Windows.Data;assembly=System.Windows" >
<local:MainViewModel.AllItems>
<local:ItemModel
FullName="Dummy"
Image="/Images/dummy.png" />
</local:MainViewModel.AllItems>
<local:MainViewModel.NextItems>
How to fill the CollectionViewSource's Source?
</local:MainViewModel.NextItems>
</local:MainViewModel>
So the question I can't find an answer to is how to fill the Source for NextItems in SampleDate.xaml? Any help would be much appreciated.
if you want to show sample data in the designer I would recommend you to do it from code. There are two ways of generating sample data for the Blend Designer or the VStudio designer:
From an XML file as you do.
From a c# class -> Best option
best option.
In WPF, in windows 8 and in WP7.5 and highger, you can access a propertie called:Windows.ApplicationModel.DesignMode.DesignModeEnabled making use of it you can seed your ObservableCollection from your view model:
public class MainViewModel
{
public MainViewModel()
{
AllItems = new ObservableCollection<ItemViewModel>();
if (DesignMode.DesignModeEnabled)
{
AllItems = FakeDataProvider.FakeDataItems;
}
NextItems.Source = AllItems;
}
public CollectionViewSource NextItems
{
get;
private set;
}
public ObservableCollection<ItemViewModel> AllItems
{
get;
private set;
}
}
In this way, if you change the model, you dont' have to regenerate an XML file, it's a little bit cleaner from a C# file. The FakeDataProvider is an static class where all design-time fake data are stored. So in you XAML the only thing you have to do is to bind your Listbox to the collection of your ViewModel.
all. I have an app that scans a picture folder and displays the images along with their names in a listbox. Each image and image name (displayed in a textblock next to the image) is stored in a horizontal stackpanel inside the listbox.
I've been trying all afternoon to find a way of displaying the image name in a textbox when the user selects it in the listbox. Sounds very simple, and I'm sure it is, but I can't seem to get it to work.
Can anyone point me in the right direction as to the best way of doing this? Thanks.
Here is my xaml if it helps:
<Grid>
<ListBox ItemsSource="{Binding AllImages}" Margin="0,0,262,0" Name="listBox1" MouseLeftButtonDown="listBox1_MouseLeftButtonDown">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Width="50" Height="50" Margin="6"/>
<TextBlock Text="{Binding Name}" Margin="6" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Height="23" HorizontalAlignment="Left" Margin="265,148,0,0" Name="textBox1" VerticalAlignment="Top" Width="198" />
</Grid>
And my code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public class MyImage
{
private ImageSource _image;
private string _name;
public MyImage(ImageSource image, string name)
{
_image = image;
_name = name;
}
public override string ToString()
{
return _name;
}
public ImageSource Image
{
get { return _image; }
}
public string Name
{
get { return _name; }
}
}
public List<MyImage> AllImages
{
get
{
List<MyImage> result = new List<MyImage>();
string filePath = #"D:\Projects\Visual Studio 2010\WpfApplication5\WpfApplication5\bin\Debug\ImageFolder";
string[] files = Directory.GetFiles(filePath);
foreach (string filename in files)
{
try
{
result.Add(
new MyImage(
new BitmapImage(
new Uri(filename)),
System.IO.Path.GetFileNameWithoutExtension(filename)));
}
catch { }
}
return result;
}
}
}
Take a look at this question.
How do I bind a Listview SelectedItem to a Textbox using the TwoWay mode?
In your case use
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="265,148,0,0"
Name="textBox1"
VerticalAlignment="Top" Width="198"
Text="{Binding SelectedItem.Name, ElementName=listBox1}"/>
To retrieve the selected image from code, you have at least 3 options (I assume your images are represented by a class ImageItem)
Set IsSynchronizedWithCurrentItem to true on your ListBox, and use the following code to retrieve the selected item:
ICollectionView view = CollectionViewSource(AllImages);
ImageItem selectedImage = (ImageItem)view.CurrentItem;
Bind the SelectedItem of the ListBox to a property in your DataContext:
<ListBox .... SelectedItem="{Binding SelectedImage}">
Access the SelectedItem property directly from code-behind:
ImageItem selectedImage = (ImageItem)listBox1.SelectedItem;
Of course, if you just want to show the name in a TextBlock, you can use Russell Troywest's solution
I have areally wierd problem when i'm using some simple custom control i've built:
this is the custom control code :
public partial class ToolButton : Button
{
public string ToolID
{
get { return (string)GetValue(ToolIDProperty); }
set { SetValue(ToolIDProperty, value); }
}
public static readonly DependencyProperty ToolIDProperty =
DependencyProperty.Register("ToolID", typeof(string), typeof(ToolButton), new UIPropertyMetadata(""));
public ToolButton()
{
InitializeComponent();
}
}
Now when i'm trying to ude this custom control in the main window like that :
<ItemsControl Margin="100" ItemsSource="{Binding Path=Students}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<customControls:ToolButton Height="100" Width="100" Margin="10" Content="{Binding Value.Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And the binding doesn't work !!
but when i'm using simple button the binding works excellent..
is someone faced similliar problem ??
Thanks...
put the following line into your ToolButton's constructor:
this.DataContext = this;