I'm developing a windows phone 8.1 (winRT) app with MVVM Light and a PCL which use a MapControl.
I'm having an issue with the binding of the MapControl Center property.
At the initialization of the app, the property is set in the ViewModel and the map is correctly centered.
However, when I update the value in the ViewModel, the map is not re-centered but if I bind the value to a textblock it's correctly updated.
XAML :
<Maps:MapControl BorderThickness="2" BorderBrush="Black"
x:Name="Map"
HorizontalAlignment="Right" Margin="0,45,0,0"
VerticalAlignment="Top"
Height="595" Width="400"
ZoomLevel="10"
LandmarksVisible = "False"
TrafficFlowVisible = "False"
PedestrianFeaturesVisible = "False"
Center="{Binding Path=ViewStoreModel.CenterPosition, Mode=OneWay, Converter={StaticResource NormalizedAnchorPointConverter}}"
MapServiceToken="{StaticResource MapServiceTokenString}">
<Maps:MapItemsControl x:Name="MapIcons" ItemsSource="{Binding ViewStoreModel.ListStoreSearch}" >
<Maps:MapItemsControl.ItemTemplate>
<DataTemplate x:Name="Temp" >
<StackPanel Tapped="Image_Tapped" x:Name="MyStack" Maps:MapControl.Location="{Binding store_position, Converter={StaticResource GeoPointConvertCenter}}">
<Image x:Name="PinsImage" Source="ms-appx:///Assets/map-pin-button.png" />
</StackPanel>
</DataTemplate>
</Maps:MapItemsControl.ItemTemplate>
</Maps:MapItemsControl>
</Maps:MapControl>
Property of the ViewModel:
public Location CenterPosition
{
get
{
return _centerPosition;
}
set
{
Set(CenterPositionPropertyName, ref _centerPosition, value);
}
}
public class Location : ObservableObject
{
public const string latitudePropertyName = "latitude";
public const string longitudePropertyName = "longitude";
private double _latitude;
private double _longitude;
public double latitude
{
get
{
return _latitude;
}
set
{
Set(latitudePropertyName, ref _latitude, value);
}
}
public double longitude
{
get
{
return _longitude;
}
set
{
Set(longitudePropertyName, ref _longitude, value);
}
}
The property Center is a type Geopoint so I use a converter to convert it from the custom class Location.
Center is a dependency property so it should be bindable.
Thanks for your help.
I figured out there are some bugs in the MapControl.
The problem comes from the binding mode. OneWay seems to work like OneTime (only at init).
If I put TwoWay it works but the map is updating the ViewModel continuously.
As a workaround, I specified that we have to tell explicity when the XAML has to update the source.
Code:
<Maps:MapControl BorderThickness="2" BorderBrush="Black"
x:Name="Map"
HorizontalAlignment="Right" Margin="0,45,0,0"
VerticalAlignment="Top"
Height="595" Width="400"
ZoomLevel="10"
LandmarksVisible = "False"
TrafficFlowVisible = "False"
PedestrianFeaturesVisible = "False"
Center="{Binding ViewStoreModel.CenterPosition, Mode=TwoWay, UpdateSourceTrigger=Explicit, Converter={StaticResource NormalizedAnchorPointConverter}}"
MapServiceToken="{StaticResource MapServiceTokenString}"
>
We're using a bit of a different approach.
We used the DataContext's PropertyChanged event - in the handler we're checking if e.PropertyName equals CurrentLocation and if it does, I'm calling NearbySitesMap.SetView(currentLocation, ZoomLevel, NearbySitesMap.Heading);.
This way, when CurrentLocation changes, the map gets a nice and smooth animation to the new location.
Have you try to remove "Path" property and bind it directly to your ViewModel?
<Maps:MapControl Center="{Binding ViewStoreModel.CenterPosition, Mode=OneWay, Converter={StaticResource NormalizedAnchorPointConverter}}"/>
Related
I have a custom UserControl called ReferencedItem. It should take a Guid called ItemId. It is implemented as such:
private static void OnItemIdChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs dpArgs)
{
//Do something
}
public static readonly DependencyProperty ItemIdProperty = DependencyProperty.Register("ItemId", typeof(Guid?), typeof(ReferencedItem), new FrameworkPropertyMetadata(
// use an empty Guid as default value
Guid.Empty,
// tell the binding system that this property affects how the control gets rendered
FrameworkPropertyMetadataOptions.AffectsRender,
// run this callback when the property changes
OnItemIdChanged
));
public Guid? ItemId
{
get { return (Guid?)GetValue(ItemIdProperty); }
set { SetValue(ItemIdProperty, value); }
}
public ReferencedItem()
{
InitializeComponent();
ViewModel = new ReferencedItemCtrlViewModel();
DataContext = ViewModel;
}
The ItemsSource will be made up of Reference objects defined as:
public class Reference
{
public Guid Id { get; set; }
}
Now when binding this ReferencedItem the value is not set as intended. Here is the code I want to work, but does not bind as intended:
<ItemsControl x:Name="ReferenceStack" ItemsSource="{Binding References}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ReferencedItem ItemId="{Binding Id}" Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have tried:
<local:ReferencedItem ItemId="128d48f0-f061-49fb-af49-b8e4ef891d03" Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
This works as expected, the OnItemIdChanged method is triggered.
<Label Content="{Binding Id}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="30" Width="90"/>
This works as expected, a label is rendered with the Id.
Is there something I'm missing here? From what I can tell the data is available at bind time -- it just doesn't bind under the exact conditions I need it to :)
Thanks for any input!
EDIT:
Here is the code-behind for ReferencedItemList, the first block of XAML posted above:
public partial class ReferencedItemList : UserControl
{
protected ReferencedItemListCtrlViewModel ViewModel;
public ReferencedItemList()
{
InitializeComponent();
ViewModel = new ReferencedItemListCtrlViewModel();
DataContext = ViewModel;
}
public void Load(Guid id, string name)
{
ViewModel.Load(id, name);
//ReferenceStack.ItemsSource = ViewModel.References;
}
}
The commented line has been experimented with in place of the ItemsSource="{Binding References}" that was defined in the XAML.
I don't think I can successfully post the code for ReferencedItemListCtrlViewModel without going down a rabbit hole -- needless to say it has a property References of type ObservableCollection<Reference> where Reference is defined earlier in this post.
ReferencedItem.xaml:
<v:BaseUserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</v:BaseUserControl.Resources>
<StackPanel Orientation="Horizontal">
<Image x:Name="LinkIcon" Visibility="{Binding HasReference, Converter={StaticResource BooleanToVisibilityConverter}}" ToolTip="View Referenced Item" Source="/Images/link.png" Height="18" MouseUp="LinkIcon_MouseUp"/>
<TextBlock x:Name="ReferencedObjectDesc" Text="{Binding ReferenceHierarchy}" FontStyle="Italic" VerticalAlignment="Center" />
</StackPanel>
I just wanted to post the answer (explanation) I came across.
The problem was changing the DataContext of my ReferencedItem user control in the constructor. The view would instantiate a ReferencedItem and alter the DataContext - so once it was time to bind I had already flipped the context from the intended Reference.
There are multiple ways to resolve the timing - all project dependent. Either avoid setting the DataContext all together, set it post binding, or change the context on other items as appropriate.
Much thanks to Sinatr, Andrew Stephens, and Mike Strobel for all mentioning this at one point or another -- just took me some time to actually reach it. I don't think there's a way to assign credit to a comment, but let me know if there is.
I'm new to wpf, I created a listbox it will create a dynamic listitems,Here I used datetemplate which contains two controls that is two textblocks, one textblocks contains binding a values form combobox(which is string datatype),The other one is, bind a value from code bind.
XAML
<ListBox ItemsSource="{Binding obj}" HorizontalContentAlignment="Left" x:Name="lstbxindex" SelectionMode="Extended" Foreground="White" FontSize="20px" Height="201" BorderBrush="#555555" Margin="80,40,0,0" VerticalAlignment="Top" Width="282" Background="#555555" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5" >
<TextBlock Height="40px" Width="80px" Text="{Binding roundedhourvalue, FontSize="24" Background="#555555" Foreground="White"></TextBlock>
<TextBlock x:Name="items" Text="{Binding}" Margin="35,0,0,0"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C# (Roundedhour.cs)
public class Roundedhour
{
public string hourvalue { get; set; }
public override string ToString()
{
return string.Format("{0}", hourvalue);
}
}
In this class create a property for hourvalue. For this class I created a object in codebehind file which I mentioned below.create a object and assign a value for hourvalue variable.
C# (Code Behind)
{
if (dispatcherTimer1.Interval == TimeSpan.FromSeconds(15))
{
//lstbxindex.Items.Add(lstbxindex.SelectedItem.ToString());
string hrvalue = Convert.ToString(hrvalueinitially);
obj = new Roundedhour();
obj.hourvalue = Convert.ToString(hrvalueinitially);
string roundedhourvalue =obj.hourvalue;
this.DataContext = this;
//lblprojectAhr.Content = string.Join(",", hrvalueinitially + "" + "hr");
}
}
Here, I created a object for Rounderhour class.Assign values to that property hour value. But I cannot be bind a value from codebind to XAML page.
Your ItemsSource should be of an CollectionType.
ListBox ItemsSource="{Binding obj}"
You should also start to give your variables and properties meaningful names. That makes it easier to read your code later on.
The second Problem is in your Binding itself.
You are binding like this: Text="{Binding roundedhourvalue}
So WPF is expecting a property roundedhourvalue on obj.
But as shown in your CodeBehind there is only obj.hourvalue.
So change your Binding to Text="{Binding hourvalue}
Check your Output-Window for details.
NOTE:
string roundedhourvalue = obj.hourvalue;
has no getter and is not accsessible since its private.
NOTE: You either use a Binding OR your set the ItemsSource in CodeBehind.
Try it like this:
Just remove all the formatting and stuff:
<ListBox ItemsSource="{Binding RoundedHours}" x:Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5" >
<TextBlock Text="{Binding hourvalue}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And change your code-behind to this:
private void UpdateDataContext(object hrvalueinitially)
{
List<Roundedhour> hours = new List<Roundedhour>();
hours.Add(new Roundedhour()
{
hourvalue = hrvalueinitially.ToString()
});
//Set the ItemsSource in code: => remove your ItemsSource Binding from XAML
listBox.ItemsSource = hours;
}
OR your can use an 'MVVM' approach:
public class MyViewModel : INotifyPropertyChanged
{
//IMPLEMENT INotifyPropertyChanged HERE PLS
public ObservableCollection<RoundedHour> Collection { get; set; } = new ObservableCollection<RoundedHour>();
private void AddToCollection(object hrvalueinitially)
{
Collection.Add(new RoundedHour()
{
hourvalue = hrvalueinitially.ToString()
});
OnPropertyChanged("Collection");
}
//Make sure to set your Windows DataContext to an Instance of this Class
}
Assign XAML object's "ItemsSource" property with your binding variable.
Also it's totally wrong binding object's itself into object's property like
this.DataTemplate = this;
Use:
List<yourobject> bindingObjectList = new List<yourobject>();
// insert your objects into the list
this.ItemsSource = bindingObjectList;
Here you can find an example:
Grid & Pivot Binding Example for multiple DataTemplates
This question is similar to Windows Phone 8.1 Toggling the visibility of a TextBlock in a DataTemplate
and countless others, but none of these ideas are working. The Loaded event is never triggered after I add my Textblock to my datatemplate in my hub. The Visual Tree search is not finding my TextBlock.
I have tried a basic binding like this:
<HubSection Background="{StaticResource HubSectionBackgroundBrush}"
MaxWidth="{x:Bind DesiredHubSectionWidth, Mode=OneWay}"
Header="You have selected:" Padding="60"
>
<DataTemplate x:DataType="local:Scenario4">
<TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left" Text="{Binding Item}"/>
</DataTemplate>
</HubSection>
with
public string Item { get; set; }
Item = makeText.Text;
But this doesn't work (Text on the Hub is always empty). From looking at previous posts and code I have come up with this xaml code using Dependency Properties:
<HubSection Background="{StaticResource HubSectionBackgroundBrush}"
MaxWidth="{x:Bind DesiredHubSectionWidth, Mode=OneWay}"
Header="You have selected:" Padding="60"
>
<DataTemplate x:DataType="local:Scenario4">
<TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left" Text="{x:Bind DesiredSelectionText, Mode=OneWay}"/>
</DataTemplate>
</HubSection>
with this in the c#
private static DependencyProperty s_desiredHubSectionWidthProperty
= DependencyProperty.Register("DesiredHubSectionWidth", typeof(double), typeof(Scenario4), new PropertyMetadata(560.0));
private static DependencyProperty selectionText = DependencyProperty.Register("SelectionText", typeof(string), typeof(Scenario4), new PropertyMetadata("Nothing"));
public static DependencyProperty DesiredHubSectionWidthProperty
{
get { return s_desiredHubSectionWidthProperty; }
}
public static DependencyProperty DesiredSelectionTextProperty
{
get { return selectionText; }
}
public string DesiredSelectionText
{
get { return (string)GetValue(selectionText); }
set { SetValue(selectionText, value); }
}
public double DesiredHubSectionWidth
{
get { return (double)GetValue(s_desiredHubSectionWidthProperty); }
set { SetValue(s_desiredHubSectionWidthProperty, value); }
}
and I set the text with
DesiredSelectionText = makeText.Text;
The width binding works perfectly, but the text is not updating. What is the proper way to change Hub/DataTemplate Text at Runtime? Since the Hub is not even printing "Nothing", something must be really wrong.
As a last resort I am thinking I will just construct my own datatemplate and assign it at runtime, but the only code I can find for that is deprecated(uses FrameworkElementFactory).
I was trying to assign to the textblock in the OnNavigatedTo method which is called before the textblock's Loaded method. That's why my program was showing the textblock as null.
Turns out the link I posted is the solution, just for me, the textblock was loaded after the page was navigated to.
I submitted this accidently, I fixed it myself and wasn't going to submit after writing out the question. But have learnt from the comments, thanks!
I am trying to create a simple todo app in win8 and eventually want to hock it into ToDoIst API.
I have created a simple task class to try and get my head around the databinding however I just can not get it to do what I want to do. I have used listboxes and other basic form elements.
task.cs
class task
{
private string content;
private bool complete;
public string Content
{
get {return content;}
set { content = value; }
}
public bool Complete
{
get { return complete; }
set { complete = value; }
}
public task(string content)
{
Content = content;
Complete = false;
}
}
MainPage.xaml
And at the moment my XAML looks like this.
<GridView HorizontalAlignment="Left" Margin="482,190,0,0" VerticalAlignment="Top" Width="400" Height="500">
<ListView x:Name="LVtasks" HorizontalAlignment="Left" Height="500" VerticalAlignment="Top" Width="400" ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Content}"/>
<RadioButton/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</GridView>
I have put in some dummy data, 4 elements and when I run it, it comes up with 4 boxes with radio buttons however no text (there is space for the text) I am not sure how I would bind the bool?
I can not see what I am doing wrong. If anyone could help and point me in the right direction, I have searched a fair amount of tutorials and just can not figure it out.
Your code looks a little strange, maybe this is what you want:
<ListView x:Name="LVtasks" HorizontalAlignment="Left" Height="500" VerticalAlignment="Top" Width="400" ItemsSource="{Binding ToDoItems}">
<ListView.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="ToDos" Content="{Binding Content}" IsChecked="{Binding IsComplete}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Do you really want radiobuttons? I think you want Checkboxes, the difference is that when you use radiobuttons only one in a group can be 'checked'
I used this code behind to have a datacontext:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ToDoItems = new ObservableCollection<TodoItem>(new List<TodoItem>
{
new TodoItem("Content1"),
new TodoItem("Content2")
});
this.DataContext = this;
}
public ObservableCollection<TodoItem> ToDoItems { get; set; }
}
I changed the name of task to ToDoItem Task is already a class in the framework and might cause confusion.
For the RadioButton IsChecked is the property to bind to a bool property:
<RadioButton IsChecked="{Binding Path=Complete}"/>
Your text is most likely not showing up because you haven't set up any change notifications and the binding is happening before you set the Content values. Using the INotifyPropertyChanged interface is the most common and usually the easiest way to do this.
Like John already mentioned you should really let the window containing your listview implement the INotifyPropertyChanged interface. And to set the data context of your window like Johan said. It is important that you call the propertychanged method in each setter of a property. It is also useful to use an ObservableCollection as ItemSource of your listview. Try to create an instance of ObservableCollection, create a property for it calling propertychanged method in its setter an set rhe ItemSource of your listview to the property. Do not forget to also call propertychanged whenever you add or remove items from the collection
I am attempting to write my first implementation of databinding in a WPF form.
I have got to the point where I can successfully retrieve data to populate my combobox with the required entries but am stuck on how I would go about populating a dependent ListView given the current selection of the Combobox.
To explain my problem better this is the current view backing my application.
class SchoolBookViewModel
{
public SchoolBookViewModel() { }
//Problem is here... How do I pass in the SelectedSchoolClassID?
public CollectionView ClassBookListEntries(Guid SelectedSchoolClassID)
{
return new CollectionView(SchoolQuoteList.GetSchoolClassQuoteListBySchoolClassID(SelectedSchoolClassID, 2011, "MyConnectionString"));
}
public CollectionView SchoolEntries
{
get
{
return new CollectionView(SchoolList.GetSchoolList("MyConnectionString"));
}
}
}
And this is the XAML
<StackPanel Height="449" HorizontalAlignment="Left" Margin="12,12,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="650" DataContext="{Binding}">
<Label Content="School:" Height="28" Name="lblSchoolName" Width="651" />
<ComboBox Height="23" Name="cmbSchoolNames" Width="644" DisplayMemberPath="SchoolName" ItemsSource="{Binding Path=SchoolEntries}" SelectedValuePath="SelectedSchoolClassID" SelectionChanged="cmbSchoolNames_SelectionChanged" />
<Label Content="Class booklist:" Height="29" Name="label1" Width="651" />
<ListView Height="163" Name="lblClassBookList" Width="645" ItemsSource="{Binding Path=ClassBookListEntries}" DisplayMemberPath="QuoteReference" />
</StackPanel>
And in the Window_Loaded method I call
stackPanel1.DataContext = new Views.SchoolBookViewModel();
Am I even on the right track? Any guidance would be appreciated.
To get the ComboBox's selection back into the ViewModel you need a property to bind to one of its selection properties. You can also get rid of the explicit CollectionViews if you're not doing anything with them. By just binding to the collections directly, ICollectionView instances will be created and managed for you automatically. Try structuring your VM like this:
class SchoolBookViewModel : INotifyPropertyChanged
{
private SchoolList _selectedSchoolClass;
public SchoolList SelectedSchoolClass
{
get { return _selectedSchoolClass; }
set
{
_selectedSchoolClass = value;
ClassBookListEntries = SchoolQuoteList.GetSchoolClassQuoteListBySchoolClassID(_selectedSchoolClass.Id, 2011, "MyConnectionString");
NotifyPropertyChanged("SelectedSchoolClass");
}
}
private SchoolQuoteList _classBookListEntries;
public SchoolQuoteList ClassBookListEntries
{
get { return _classBookListEntries; }
set
{
_classBookListEntries = value;
NotifyPropertyChanged("ClassBookListEntries");
}
}
private SchoolList _schoolEntries;
public SchoolList SchoolEntries
{
get
{
if (_schoolEntries == null)
_schoolEntries = SchoolList.GetSchoolList("MyConnectionString");
return _schoolEntries;
}
}
...
}
In general it's better to not set explicit Width and Height values and instead let the layout system size elements for you. You can also get rid of the DataContext="{Binding}" - this is redundant as DataContext is inherited and {Binding} means the value of the DataContext itself. Here's the XAML cleaned up and bound to the new properties from above:
<StackPanel HorizontalAlignment="Left" Margin="12,12,0,0" x:Name="stackPanel1" VerticalAlignment="Top">
<Label Content="School:" x:Name="lblSchoolName" />
<ComboBox x:Name="cmbSchoolNames" DisplayMemberPath="SchoolName" ItemsSource="{Binding Path=SchoolEntries}"
SelectedItem="{Binding SelectedSchoolClass}" />
<Label Content="Class booklist:" x:Name="label1" />
<ListView x:Name="lblClassBookList" ItemsSource="{Binding Path=ClassBookListEntries}" DisplayMemberPath="QuoteReference" />
</StackPanel>