still pretty new at WPF and binding especially, but I have an enum which I will be using as properties on objects elsewhere in my project, but one of the very first kickoff points of the program will be the user selecting a single item from a combobox, which I want to match to the available enum options. I originally thought to have a dictionary object with the enum option as the key, and the value as a string for use in the UI presentation, and this is what I have been working towards. I have been searching around and thought I had it, but the combobox is populating blank.
I have a couple of questions;
Firstly, since I'm still not quite sure what is what in relation to binding, is this issue related to this post Target Exception Bug which I found in a comment on another question? If so, does this mean I'm barking up the wrong tree for the time being? And is there another way for me to achieve my goal?
Secondly, if its not related, have I missed something in the below code? I currently get no error in the output window and the project compiles fine.
Here's the enum (which lives in a separate namespace that has been added to the project references);
namespace WGM_lbr
{
public class Available_Wgms
{
private static Dictionary<Wgms,string> _wgmColl;
public static Dictionary<Wgms,string> WgmsCollection
{
get
{
return _wgmColl;
}
}
static Available_Wgms()
{
_wgmColl = new Dictionary<Wgms, string>() {
{Wgms.First, "First Dictionary item"},
//other Dictionary Items go here
}
}
public enum Wgms
{
First,
//other Enum options go here
}
}
}
My Resource declaration in app.xaml
<Application x:Class="The_First.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WGM="clr-namespace:WGM_lbr;assembly=WGM_lbr"
StartupUri="MainWindow.xaml">
<Application.Resources>
<BooleanToVisibilityConverter x:Key="b2v"/>
<WGM:Available_Wgms x:Key="WgmList"/>
</Application.Resources>
And finally the combobox and binding xaml (in case its relevant, this lives in a nest of wpf controls, up to a grid which lives on page, which is being loaded to mainwindow.xaml via a frame control using the page as the source. Both the page and mainwindow have declared the WGM namespace - I cut these out as this post is already long enough);
<ComboBox Name="cmbWgmSelector" Margin="5,0" ItemsSource="{Binding Source={StaticResource WgmList}}"/>
Any help/advice that can be provided is greatly appreciated.
Update the binding as below:
<ComboBox Name="cmbWgmSelector" Margin="5,0" DisplayMemberPath="Value" ItemsSource="{Binding Path=WgmsCollection, Source={StaticResource WgmList}}"/>
Related
I want use the MahApps PanoramaControl to display a bunch of photo's. However I see only the string paths appear in the control, which will be correct if you look at my code.
But I can't figure out howto get it working to show the images instead of the links.
Xaml:
<Controls:Panorama Grid.Row="2"
Grid.ColumnSpan="4"
ItemBox="140"
ItemsSource="{Binding PhotoCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
ViewModel:
string[] filePaths = Directory.GetFiles(#"D:\Google Drive\Images\Backgrounds");
test = new PanoramaGroup("My Photo's", filePaths);
PhotoCollection = new ObservableCollection<PanoramaGroup> { test };
Anyone an idea on how to make it show the images? The control is load as I can scroll sideways on the text.
There is not much documentation on their site on how to get it working...
Or are you using some other Metro style lib for the 4.0 framework?
In order to get MahApps Panorama control i would offer the follow solution below. As to other frameworks that provide this level of detailed MODERN UI experience I have not come across any but interested to know if you do.
Hope the solutions works for you.
You need to add a Data Template the represent the object you're showing.
Start off by defining the object in a POCO class (as illustrated below). Then in the population of the items ensure you translate them to the newly created POCO object instead of leaving them as string values.
public class Photo {
public string Path { get; set; }
}
Next in the definition of you XAML window you need to create a reference to the namespace where the POCO object resides.
xmlns:model="clr-namespace:project.models;assembly=project"
Last, but not least, you want to then create a DataTemplate to represent the object. This is added to the window resources.
<Window.Resources>
<DataTemplate DataType="{x:Type model:Photo}">
<Image Source="{Binding Path}"/>
</DataTemplate>
</Window.Resources>
This should then let the render take place in the interface where it belongs. Hope this works for you.
I basically want to take a bunch of names in a collection and bind them to a combobox. For example:
Bill
Jack
Bob
Kevin
and have those items in a collection and have it bound to the ComboBox. I'm not sure if the list will be updated dynamically or not, but I prefer to plan for it to be. Any help would be appreciated. I've been trying for a few hours now and can't figure it out. I want to do it in XAML and not the code-behind. In the code-behind,
MyComboBox.ItemsSource = MyObservableCollection;
works fine. I don't know how to do that in XAML though with the collection declared in the code-behind.
Thanks in advance (again), community.
*EDIT:
This is how I have the collection declared and accessible.
public ObservableCollection<string> propertynames
{
get {return _propertynames;}
}
private ObservableCollection<string> _propertynames;
The last thing I tried was this:
<Window.Resources>
<CollectionViewSource Source="{Binding propertynames}" x:Key="srcSort"/>
</Window.Resources>
....
<ComboBox x:Name="cboSort" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="256" Background="WhiteSmoke" Margin="12,50,0,0" FontSize="12pt"
Height="27.28"
SelectedIndex="0"
SelectionChanged="cboWorkCenters_SelectionChanged"
ItemsSource="{Binding Path = {StaticResource srcSort}}">
</ComboBox>
....
I'm a total n00b to this stuff. Been in it about a week now, so I may have done something really obvious to a seasoned user.
*EDIT #2
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:WpfApplication1"
Title="Window1" Height="226" Width="242"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ComboBox Margin="43,71,40,77"
Name="comboBox1"
ItemsSource="{Binding ob}" />
</Grid>
</Window>
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public ObservableCollection<string> ob
{
get
{
return _ob;
}
}
private ObservableCollection<string> _ob = new ObservableCollection<string>();
public Window1()
{
InitializeComponent();
FillObj();
//comboBox1.ItemsSource = ob;
}
private void FillObj()
{
for (int i = 1; i < 6; i++)
{
_ob.Add(i.ToString());
}
}
}
}
Made above real simple project just to see if I was doing it all wrong. This worked fine so something else must be causing it to fail.
*EDIT #3
*PROBLEM FIXED
For God's sake, I figured it out. I've been on this for HOURS and it's just silly what's caused it to fail.
The solution is this: I wasn't instantiating _propertynames when I declared it. I was querying the class properties with Linq to get the list of properties and then created _propertynames by passing ...GetProperties.ToList<...>() to the constructor. Apparently, you have to instantiate the variable so it hits during InitializeComponent. Unreal.
Once I did that and then added the items to it after the fact, it worked fine.
I wish WPF had a face so I could punch it. I know it's my ignorance of how it works, but I really could have used some kind of message.
Thanks guys for the help. Both of your suggestions were useful once I took care of the root issue.
private ObservableCollection<string> _propertynames
needs to be
private ObservableCollection<string> _propertynames = new ObservableCollection<string>()
There are countless ways of doing this. Once you've created the collection in code-behind, you can:
Call Resources.Add to add it to the window's resource dictionary, and then bind to the resource, e.g. ItemsSource="{Binding {DynamicResource MyList}}".
Give the ComboBox a name (using the x:Name attribute) and set its ItemsSource explicitly in code, e.g. MyComboBox.ItemsSource = myCollection;.
Create a class, make the collection a property of the class, and set the window's DataContext to an instance of that class and bind to it directly, e.g. ItemsSource = "{Binding MyCollectionProperty}".
Make the collection a property of the window, set the window's DataContext to this, and bind to the property (this is essentially the same technique as #3, only you're not creating a new class).
Without setting the window's DataContext, you can still reference a property on it using binding as long as you've given it a name, e.g. {Binding ElementName=MyWindow, Path=MyCollection}. (This is the same as Ross's suggestion.)
Or, without giving the window a name, you can use RelativeSource binding to find the ancestor Window and bind to a property on it. I don't have any confidence in my ability to write a working binding expression that uses RelativeSource off the top of my head, so I'll leave that as an exercise for the reader.
You can set the DataContext of the ComboBox to the instance of your collection, and then set itsItemsSource to {Binding}. You probably wouldn't do this in practice; I mention it just because it seems to be a common mistake for people to set the DataContext of a control without also setting a binding, and then wonder why content from the bound object isn't showing up.
(While I've said "window" in the above, everything I've said is also true for user controls.)
I'm sure there are at least five other ways to do this that I'm not thinking of. Binding is really, really flexible.
What have you tried so far?
I would approach it as follows, assuming the combo box is within a UserControl with a code-behind class containing the public property MyObservableCollection:
<UserControl x:Name="MyCollectionOwnerControl">
<ComboBox ItemsSource="{Binding ElementName=MyCollectionOwnerControl, Path=MyObservableCollection, Mode=OneWay}" />
</UserControl>
I am fairly new to WPF, have been working on finding an answer to this for a couple days without much luck, it seems like there should be a way. I have set up a DataTemplate whose DataType is a custom class of mine. Within the DataTemplate definition, I have set up a resources collection using . I did this because I want to create an ObjectDataProvider that will be available to the controls in the DataTemplate - I want the ObjectInstance of this ObjectDataProvider, to be currently bound data item (teh current instance within a list, of my custom class) - because then I want to be able to run a method on the current data instance - when the user changes the text in a textbox that is part of the DataTemplate. Hard to explain but this should make it clearer, here is my xaml:
<DataTemplate x:Key="TierDisplay" DataType="{x:Type tiers:PopulatedTier}">
<DataTemplate.Resources>
<ObjectDataProvider x:Key="FilteredItems" MethodName="GetDisplayItems">
<ObjectDataProvider.MethodParameters>
<sys:Int32>0</sys:Int32>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</DataTemplate.Resources>
<StackPanel Orientation="Vertical">
<TextBox Name="txtMaxSupplyDays" LostFocus="txtMaxSupplyDays_LostFocus"></TextBox>
<DataGrid ItemsSource="{Binding Source={StaticResource FilteredItems}}" />
</StackPanel>
</DataTemplate>
Each instance of the DataTemplate is bound to an instance of the PopulatedTier class. When the user leaves the textbox, txtMaxSupplyDays, I have code in the code-behind to take the value they have entered, and put it into the first MethodParameter of my ObjectDataProvider (whose key is FilteredItems). This works fine using the C# code-behind below, the code finds FilteredItems and plugs the desired value into the MethodParameter. But I can't figure how to tie FilteredItems into the current instance of PopulatedTier so that its GetDisplayItems will run. (If this worked, then presumably the DataGrid would refresh, using the output of GetDisplayItems as its ItemsSource.) In fact, in the C# below, it finds/recognizes the DataContext property of the textbox (sender) as being an instance of PopulatedTier. But how can I refer to this in the XAML within the ObjectDataProvider definition? THANK YOU and let me know if I can clarify further. Of cousre alternate suggestions are welcome; I'd like to keep as much in the XAML and out of the code-behind as I can.
private void txtMaxSupplyDays_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
int value;
bool valueOK = Int32.TryParse(textBox.Text, out value);
if (valueOK)
((ObjectDataProvider)textBox.FindResource("FilteredItems")).MethodParameters[0] = value;
}
You have right thoughts about your code-behind - it have to be as small as possible. Its one of the slogan of MVVM pattern, that is what you need - learn MVVM. Internet have a lot of resources, so it wouldn't be a problem to find it.
I'm having a problem accessing a Panel control defined on the XAML of a page, the XAML is defined this way:
<UserControl
x:Class="PhoneBook.SilverlightMainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" Width="400" Height="300" d:DesignWidth="993" d:DesignHeight="887">
<Grid x:Name="LayoutRoot" />
</UserControl>
The class is defined like this:
public partial class SilverlightMainPage : UserControl
{
public SilverlightMainPage()
{
InitializeComponent();
}
}
And I'm trying to instantiate it this way:
var silverlightMainPage = new PhoneBook.SilverlightMainPage();
SomeMethod((silverlightMainPage.LayoutRoot);
What I find strange is that when I put the dot after the object instance, it actually list LayoutRoot as one of the members, but when I try to compile the application it says that there's no member with that name.
Any ideas of what can be hapenning?
Thanks
EDIT: I also tried creating a property on the SilverlightMainPage class that returned the LayoutRoot element, but it also says that the class doesn't contain a definition for Layout root.
Is there any chance that you're trying to access SilverlightMainPage.LayoutRoot from a different assembly? In the MainPage.g.i.cs file, LayoutRoot (and all other controls defined in XAML) are marked "internal", i.e.:
internal System.Windows.Controls.Grid LayoutRoot;
You might want to try creating a public rather than an internal property that does a FindName("LayoutRoot") and returns the appropriate control.
Actually I found the problem.
I was generating the project automatically with a tool built by someone else in the company.
I did some extra tests and added a new UserControl to the project and tried to access the LayoutRoot from a property in the code behind, and it worked.
Then copied the exact same code to the file with the problem (just changing the class name) and it didn't compile.
Then I checked the project file, and found a section like this:
<ItemGroup>
<Compile Include="SilverlightMainPage.xaml.cs">
<DependentUpon>SilverlightMainPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
Which for some reason was causing the compilation to fail.
I removed that section and now everything works fine.
Thanks for your answers though.
I'd like to programmatically access static resources much as I would in XAML:
<TextBlock Text="{Binding Source={StaticResource My.Text.Key}}" />
This works whether my static resource is defined on the TextBlock, some parent element (e.g. UserControl) or even the application. It seems that either the StaticResource binding expression knows how to walk up the element tree, or the element itself does. I'd like to do the same thing programmatically:
<UserControl x:Class="MyCustomControl" ...>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml"/> <!-- Sets 'My.Text.Key' to System.String 'Hello, World!' -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
</UserControl>
public partial class MyCustomControl
{
public MyCustomControl()
{
InitializeComponent();
string myCustomValue = this.Resources[MyCustomValue] as string; // myCustomValue becomes null!
}
}
Even in this simple test, my resource can't seem to be accessed programmatically. And this is the simplified version of what I was trying to really do: find a static resource via an element that I have a custom dynamic property attached to (e.g. uiElement.Resources[key]).
Despite your comment to the contrary I doubt the use of "." in your resource key is really the source of your problem. In this situation the "." has no special meaning and would not impact how the resource is accessed. (I've tried and failed to reproduce any problem with it).
There is though a very big difference between using the {StaticResource MyName} mark up extension and an attempt to find the resource programmatically.
The markup extension causes the XamlParser to look for the specified key the Resources property of the FrameworkElement to which the property being assigned belongs. If the key is not found it looks for it in the parent FrameworkElement and it keeps going until it reaches the root FrameworkElement. If it is still not found it has a look in the Application's Resources property.
On the other hand this code:-
string myCustomValue = this.Resources[MyCustomValue] as string;
sf just looking in the single Resources property for the user control. No attempt is made to hunt down the key in ancestors or in the application resources. Its a simple Dictionary lookup. This I suspect is what was really tripping you up.
Having said that I would say the using "." in a resource key may not be a good idea. The "." does have meaning in various XAML scenarios already so using it in key names as well has the potential to confuse a developer reading the code even though Silverlight is quite happy with it.