I'm new to WPF and I want to do an app which shows pictures in a ListBox.
I made a class called _Images, that gets images from a local folder, puts on a list.
namespace wpfAppSlides.Imagens
{
class _Images
{
public List<Image> imgList = new List<Image>();
public void CarregaImagens()
{
string dir = #"C:\Users\devUser\Img";
foreach (string file in Directory.GetFiles(dir))
{
Image image = new Image();
BitmapImage source = new BitmapImage();
source.BeginInit();
source.UriSource = new Uri(file, UriKind.Relative);
source.EndInit();
image.Source = source;
imgList.Add(image);
}
}
}
}
An then, in my XAML, I referenced my namespaces.
xmlns:is="clr-namespace:wpfAppSlides._Images"
xmlns:col="clr-namespace:wpfAppSlides"
And put in a DataContext.
<Window.DataContext>
<is:_Images></is:_Images>
</Window.DataContext>
But when I wanna feed my ListBox with the items on imgList, using ItemsSource (in Properties, not code), he does not find any Path that I can relate to imgList.
ListBox XAML:
<ListBox Height="110"
HorizontalAlignment="Left"
Margin="14,50,0,0"
Name="listBox1"
VerticalAlignment="Top"
Width="477" ItemsSource="{Binding}" />
MainWindow code:
public partial class MainWindow : Window
{
_Imagens imgs;
public MainWindow()
{
InitializeComponent();
imgs = (_Images)this.DataContext;
imgs.CarregaImagens();
}
}
I tried to use ObservableCollection, but it's no use.
Help? ):
I noticed a couple of things..
looks like your class _Images isn't declared as public
When constructor of that class is called (happens when you added it as Window.DataContext,
how/where does CarregaImagens() get called. Does it ever get called? If not, put in in the default constructor.
You shouldn't name a class with the underscore (that's the convention for a private member)
you know that in order to bind in xaml, the property cannot be private?
hoper these help...
Related
Basically, my question appears already in the title:
When I have a MainWindow like following:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:easycache"
xmlns:map="clr-namespace:MapControl;assembly=MapControl.WPF"
x:Class="easycache.MainWindow"
x:Name="MainWindow1"
Title="easycache"
Height="600"
Width="850">
<map:Map Grid.Column="2"
Grid.Row="1"
Name="map"
IsManipulationEnabled="True"
LightForeground="Black"
LightBackground="White"
DarkForeground="White"
DarkBackground="#FF3F3F3F"
Center="0.0,0.0"
ZoomLevel="0.0"
TileLayer="{Binding Source={StaticResource TileLayersView}, Path=CurrentItem}" />
In My Window 2, I have:
<map:Map Name="map" IsManipulationEnabled="False"
LightForeground="Black" LightBackground="White" DarkForeground="White" DarkBackground="#FF3F3F3F"
Center="0.0,0.0" ZoomLevel="{Binding ?????}">
And I want to bind the ZoomLevel of the map in Window2 to the ZoomLevel of the map in my MainWindow. How can I achieve this?
Regards,
Ialokim
In your MainWindow codebehind create the a public property with a public get accessor, something like this:
private double _mapZoom;
public double MapZoom //I assume that ZoomLevel is of type double, if not use the correct type
{
get { return _mapZoom; }
set { _mapZoom = value; OnPropertyChanged("MapZoom"); }
}
You'll have to implement the INotifyPropertyChanged interface in your MainWindow (there is a million examples everywhere, no need to get into that here).
Then, bind your map's ZoomLevel property to the MapZoom property, like this:
<map:Map Name="map" ZoomLevel="{Binding RelativeSource={RelativeSource Self}, Path=MapZoom}, Mode=TwoWay"/>
For this to work, the DataContext of the Window must be the Window itself, in you need one line in the Window's constructor:
public MainWindow()
{
InitializeComponent();
this.DataContext = this; //add this line
}
You need a way for the second window to call the main window, so you can create a static property to return the current instance in the mainwindow:
private static MainWindow _instance;
public static MainWindow Instance
{
get
{
if (_instance == null)
_instance = new MainWindow();
else
_instance = this;
return _instance;
}
} //you have to make sure to create ONE instance of MainWindow before getting this property, and not to create more instances elsewhere
Now you'll have the MainWindow exposing a public property, which is bound to the map's zoom.
So, in your second Window create a property pointing at that instance of the main Window:
public MainWindow Main { get { return MainWindow.Instance; }
Finally, you can bind the zoom of the second map to the public property MapZoom which is a member of Main:
<map:Map Name="map2" ZoomLevel="{{Binding RelativeSource={RelativeSource Self}, Path=Main.MapZoom}, Mode=TwoWay"}">
In this binding "Main" is referring to the MainWindow instance through the public property of window2 (set the this.DataContext = this; in window2 too), and accessing the .MapZoom property.
By default, all objects you create in XAML are private, so you have to expose them explicitly with public properties with get/set accessors to make them accesible from outside.
Let me know if this works, I did not create an app to test the code. Regards!
I have a custom class:
class CustomClass
{
public string Test { get; set; }
CustomClass()
{
this.Test = "";
}
}
I'm declaring this custom class on a Application.Resources like that:
<Application.Resources>
<local:CustomClass x:Key="myobj"/>
</Application.Resources>
This resource is the DataContext of a grid and the TextBox binds the Test property, like that:
<Grid DataContext="{DynamicResource myobj}">
<TextBox Text="{Binding Path=Test, Mode=TwoWay}"></TextBox>
</Grid>
Suddenly at run-time, I change the value of the resource
this.Resources["myobj"] = new CustomClass() { Test = "12456" };
I want the value referenced on TextBox be always the value of the object that is currently on "myobj" resource, and I want change automatically the value of the current object when the value of Text property of the TextBox is changed, because of this, I used the Mode=TwoWay, but it's not happening.
I used WPF Inspector and I saw when the resource value is changed, binds a new cleared object and not my created object
I'm new in WPF sorry my english and my unknowledge;
Regards,
EDIT 1
It works implementing the code posted by ethicallogics, thanks! But sorry if I wasn't clear before, when binds a new resource as below
this.Resources["myobj"] = new instance;
it works fine when it is called inside the same window that this resource was declared, unlike when I call this line inside a UserControl, it seems that the UserControl doesn't inherit the MainWindow Resources, how that really works ?
class CustomClass:INotifyPropertyChanged
{
private string _test;
public string Test
{
get
{
return _test;
}
set
{
_test = value;
Notify("Test");
}
}
CustomClass()
{
this.Test = "";
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void Notify(string propName)
{
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs(propName);
}
}
Use this class .I hope this will help.
public class ToolBarView : ToolBar
{
public ToolBarView()
{
this.DataContext = new ToolBarViewModel();
}
}
public ToolBarViewModel: ViewModelBase
{
public ObservableCollection<ViewModelBase> Items {get;set;}
public ToolBarViewModel()
{
// populate button view models
Items.Add(new ButtonViewModel() {Content="Button1"});
Items.Add(new ButtonViewModel() {Content="Button2"});
}
}
public class ButtonView : Button
{
public ButtonView()
{
this.DataContext = new ButtonViewModel();
}
}
public class ButtonViewModel : ViewModelBase
{
public object Content {get;set;}
}
In MainWindow.xaml
<Window.Resources>
<DataTemplate x:Key="buttonTemplate" DataType="{x:Type vm:ButtonViewModel}">
<v:ButtonView Content={Binding Content}/>
</DataTemplate>
<v:ToolBarView ItemsSource="{Binding Items}"
ItemTemplate={StaticResource buttonTemplate}/>
Note: I did INotifyChanged in ViewModelBase class
In MainWindow.xaml. i think My template is wrong.ButtonView in DataTemplate is creating a new view instance. It is not binding the viewModel that was poplulated in the ToolBar Items collection. I tried to do with Relative Binding. Still not successful.
Please help me out.
Just drop the line where you create a new VM and overwrite the DataContext:
this.DataContext = new ButtonViewModel();
Then the DataContext will be inherited (it will be the item in the collection, the ButtonVM).
(As a side-note, you seem to try view-first and view-model-first at the same time, you should stick with one. Also the view should probably already bind to all the relevant properties on the view-model so that you just need need to create the view and that's it)
I want to separate my user interface from my code, so I (obviously) landed at bindings. As a test, I've written the following XAML:
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="Auto" Width="200">
<StackPanel>
<TextBox Text="{Binding Item}"/>
<Button Content="Add" Click="AddNew"/>
<ListBox Height="100" ItemsSource="{Binding Items}"/>
</StackPanel>
</Window>
The C# looks like this:
namespace BindingTest
{
public partial class MainWindow : Window
{
public string Item { get; set; }
public ObservableCollection<string> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<string>();
}
private void AddNew(object sender, RoutedEventArgs e)
{
Items.Add(Item);
}
}
}
What I want to happen is that the text entered into the textbox is added to the listbox's itemssource. However, this doesn't happen...
Two things you need two do -
Set - DataContext = this; in your constructor.
You'd be better off if you would change your properties to dependency properties instead. You could do that easily with the "propdp" snippet in visual studio.
Data binding is performed against the current data context. However, you have not set the data context for your window. Often you will set the data context to a view model but in your case you simply want to use the window class for that.
You should add the following line to the constructor:
DataContext = this;
Change your code to this:
public partial class MainWindow : Window
{
public string Item { get; set; }
public ObservableCollection<string> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<string>();
DataContext = this;
}
private void AddNew(object sender, RoutedEventArgs e)
{
Items.Add(Item);
}
}
}
You do need to set your DataContext - works for me.
Two things:
You should set the correct data context for your window. Otherwise the binding will not find your properties.
You should initialize your Items collection before the InitializeComponent() call as inside it the ListBox tries to evaluate the expression and get NULL as the binding souce. And since you are not implementing INotifyPropertyChanged and the property is not a DependencyProperty the ListBox will never reevaluate the binding thus it will never get the instance of your Items collection.
So, the code should be as follows:
public MainWindow()
{
Items = new ObservableCollection<string>();
DataContext = this;
InitializeComponent();
}
Try this
hope this will work. But this is not hte right approach. You need to set the DataContext to the Object whose properties u guna use for binding. you must follow MVVM Architecture.
I have created an ObservableCollection in the code behind of a user control. It is created when the window loads:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Entities db = new Entities();
ObservableCollection<Image> _imageCollection =
new ObservableCollection<Image>();
IEnumerable<library> libraryQuery =
from c in db.ElectricalLibraries
select c;
foreach (ElectricalLibrary c in libraryQuery)
{
Image finalImage = new Image();
finalImage.Width = 80;
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri(c.url);
logo.EndInit();
finalImage.Source = logo;
_imageCollection.Add(finalImage);
}
}
I need to get the ObservableCollection of images which are created based on the url saved in a database. But I need a ListView or other ItemsControl to bind to it in XAML file like this:
But I can't figure it out how to pass the ObservableCollection to the ItemsSource of that control. I tried to create a class and then create an instance of a class in xaml file but it did not work. Should I create a static resource somehow>
Any help will be greatly appreciated.
Firstly, the ObservableCollection is a local variable. What you need to do is have it as a private global variable and expose it with a public property. You can use the INotifyPropertyChanged interface to have the image data update automagically when the actual collection itself changes.
In your XAML, you then need to set the DataContext to self, and you can then directly bind your public property to the ItemsSource. You may want to use an ItemTemplate for displaying the items in a custom manner.
Cheers,
Adam
Example as requested:
In C#:
public MyWindowClass
{
public ObservableCollection<image> MyImageCollection
{
get;
set;
}
}
In XAML:
<UserControl
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
...
<ListBox ItemsSource="{Binding MyImageCollection}" ItemTemplate="*yourtemplateresource*" />
...
</UserControl>
Now, the reason that I mentioned using INotifyPropertyChanged is that if you try:
MyImageCollection = new ObservableCollection<image>();
The items in the listbox will not automatically update. With an ObservableCollection, however, you do not need to implement INotifyPropertyChanged for basic addition and removal of list items.
You have to set the DataContext of the UserControl to your collection:
DataContext = _imageCollection
You can do that in the UserControl_Loaded() method.
Next you need to bind the ItemsSource of the ListView in the XAML:
<ListView ItemsSource="{Binding}"/>
The {Binding} is equivalent to {Binding .} which binds to the DataContext of the UserControl. If you need "more stuff" in your DataContext you can instead create a class like this:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
...
}
Use this class for the DataContext:
DataContext = new ViewModel();
And replace the binding to bind to the Images property:
<ListView ItemsSource="{Binding Images}"/>
Then you can add another property to ViewModel:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
public String Message { get { ... } set { ... } }
...
}
And bind it to a control:
<TextBlock Text="{Binding Message}"/>
Remember to fire the PropertyChanged event when the Message property is changed in ViewModel. This will update the UI when view-model properties are changed by code.