I'm trying to dynamically switch the DataContext for my application when the selection of a Pivot item changes. Everything works as I want it to, however I keep getting errors in the debugger output window about data sources not being found from the ListBoxes present inside the PivotItems that are not the currently selected PivotItem.
For instance, let's say I have 2 PivotItems - PivotItem1 and PivotItem2 - each displaying one ListBox each - ListBox1 and ListBox2. Now, when PivotItem1 is active and displaying ListBox1, ListBox2 complains about its data source not being found, which is correct because the current DataContext does not contain the collection it is bound to. This is the error (I've added extra line breaks):
System.Windows.Data Error: BindingExpression path error:
'Entries' property not found on 'MyApp.ViewModels.CategoriesView'
'MyApp.ViewModels.CategoriesView' (HashCode=79283607).
BindingExpression: Path='Entries'
DataItem='MyApp.ViewModels.CategoriesView'
(HashCode=79283607); target element is
'System.Windows.Controls.ListBox' (Name='ListBox2');
target property is 'ItemsSource' (type 'System.Collections.IEnumerable')..
Similarly, when PivotItem2 is active, ListBox1 throws an error. I'm updating the DataContext within the LoadingPivotItem event of the Pivot, I've also tried doing this within the LoadedPivotItem event but get the same error.
Both collections implement INotifyPropertyChanged, and as I mentioned at the beginning, everything works, despite the error. I'd like to suppress the error somehow.
Here's the XAML for one the ListBoxes:
<ListBox x:Name="ListBox1"
Margin="0,0,-12,0"
ItemsSource="{Binding Categories}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17"
Width="432">
<TextBlock Text="{Binding CategoryName}"
TextWrapping="Wrap"
Margin="12,0,0,0"
Style="{StaticResource PhoneTextExtraLargeStyle}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The other is identical, except "Categories" is replaced by "Entries" and "CategoryName" by "EntryName".
Thanks in advance for your help.
While the action you're performing doesn't cause any errors in your application there will be an impact on the device as a whole while the Silverlight framework handles the binding errors.
Rather than having two different data models and changing them, why not have a single model which contains the "Categories" and "Entries" models but just set the one you're not displaying to be an empty collection (or whatever as appropriate). This would allow the bindings to still work but would prevent population of the list boxes you're not displaying.
Related
I have a question. Two approaches below correctly bind selected item for listview. Approach 1 seems to be much simpler and quicker, Approach 2 seems to be more code consuming. Is there anything else that's makes difference between those two approaches or this is doing exactly the same? My only concern is whether Approach 2 works in TwoWay mode by default and can it be changed to another mode?
Thanks for the info.
Approach 1:
<ListView SelectedItem="{Binding SelectedOrder, Mode=TwoWay}" ItemsSource="{Binding Orders}">
..
</ListView>
Approach 2:
<ListView ItemsSource="{Binding Orders}" ItemSelected="OnPlaylistSelected">
..
</ListView>
This also requires this in code behind of course:
void OnPlaylistSelected(object sender, SelectedItemChangedEventArgs e)
{
_viewModel.SelectOrderCommand.Execute(e.SelectedItem);
}
Having had a quick look at the docs, I cannot find a definitive answer myself:
ListView Interactivity
If I were to hazard a guess though, I would suggest that if you are just wanting to assign something to SelectedItem and do nothing fancy, then Approach 1 is the way to go.
If however you wanted to do other things other than just set the SelectedItem, then you would go with Approach 2 as it will give you a bit more control over what is happening in the background.
Is there anything else that's makes difference between those two approaches or this is doing exactly the same?
The Approach 1 uses the TwoWay binding model, and the Approach 2 uses the Default binding model. But here the default model should also be TwoWay. Therefore, they should be the same.
Generally you don't need to go looking. Most of the controls behave as you would intuitively expect them to.
For example:
An Entry is two way by default because that's just what you would expect.
A Label is one way because... well... you can't set a label through the UI.
And so on.
The only time you really need to set binding mode explicitly is when you are deliberatly doing something out of the ordinary, like you want to use an Entry because you like the look but don't want the user to be able to enter a new value: That's a weird use-case for an entry so you probably need to explicitly set its binding mode.
And from the definition of BindingMode Enum, if using BindableProperty to bind, the default value should be OneWay.
Default : When used in Bindings, indicates that the Binding should use the DefaultBindingMode. When used in BindableProperty declaration, defaults to BindingMode.OneWay.
If you want to modify the binding model on runtime, you could use code to do that,
listview.SetBinding<StoreDetailViewModel>(ListView.ItemsSourceProperty, vm =>vm.items,mode: BindingMode.TwoWay);
I am building a Xamarin Forms Application and using XAML that is databound to a ViewModel that contains the model that is stored in Realm datastore.
When Two Way databinding it turned on the view will throw the error
Cannot set values outside transaction
Which includes
InnerException {Realms.RealmOutsideTransactionException: Cannot set
values outside transaction at
Realms.RealmObje…} Realms.RealmOutsideTransactionException
I'm not sure why the data binding is wanting to set the value back on the RealmObject when its loading the View that shows the Entry object that its databound to.
By Default the Mode=TwoWay. I have to change it to Mode=OneWay to get the view to load the databound data.
Is this a bug?
According to this article, this should work.
https://blog.xamarin.com/cross-platform-development-with-xamarin-forms-and-realm/
When you have two-way binding then the Realm needs to have an active Transaction as shown in the QuickJournal sample's JournalEntryDetailsPage.
I'm not sure why the data binding is wanting to set the value back on
the RealmObject when its loading the View that shows the Entry object
that its databound to.
This puzzles me too. It is a quirk of the way Xamarin Forms implement two-way bindings. If a string property is blank, then it doesn't trigger the setter. However, if there is a value in the RealmObject's property, it seems to fire a Xamarin.Forms.Platform.IOS.EntryRender:OnEditingChanged and attempt to set the same value back again. I consider this a bug in Xamarin Forms. It should not be propagating the unchanged value back as far as the viewmodel.
I'm trying to create a common converter that will take in a string from a resource resx file (the app has to be localizable) as a parameter.
<TextBlock
ToolTipService.ToolTip="{Binding IsInUse, ConverterParameter={Binding Path=WhereUsedIndicatorTooltips, Source={StaticResource resource}}, Converter={StaticResource whereUsedIndicatorTooltipConverter}}" />
Where resource is declared at the top of this page in XAML as:
<UserControl.Resources>
<resources:myResource x:Key="resource" />
</UserControl.Resources>
At runtime I get this exception:
System.Windows.Markup.XamlParseException:
Provide value on
'System.Windows.Data.Binding' threw an
exception. [Line: 47 Position: 42]
---> System.InvalidOperationException: Operation is not valid due to the
current state of the object.....
I'm aware from this StackOverflow question that the ConverterParameter is not bindable and is not a DependencyObject. Is there a workaround to this problem, besides putting the text in the XAML?
I found a solution from Brandon Truong. This works very well.
I put my FrameworkElement with DependencyProperty converter in:
<UserControl.Resources>
<utilConverters:myTooltipConverter x:Key="myTooltipConverter" Tooltips="{Binding Path=tooltips, Source={StaticResource resource}}" />
</UserControl.Resources>
I got same error here ,
**
ElementName=RadCalendarMonths,Path=SelectedDate,StringFormat='MMMM
yyyy',ConverterCulture={Binding Path=CurrentCultureInfo,
Source={StaticResource ResourceWrapper}}}"/>
I used Converter Culture property Binded! Opps! I can't do that because the property ConverterCulture is not a DependencyProperty. If a property is not instance of DependencyProperty you cant use binding on it.
If you look at Property(F4) Pane on VS2010 you can see that some properties support Binding some properties does not! Some properties not seen there because some properties are read only as u know.
So using resource is a logical way to solve it.
Environment: VS2010, SL4, RIA Services
I have an SL4 UI that I developed against data objects that were instantiated from an XML file (so that I didn't have to worry about the back end of the app while I worked on the front end). In this UI, I have a data grid that shows properties for each object in the collection of data. I also have a details panel that shows editable details for the object that is selected in the datagrid.
In this version that uses the "mocked" data, I have the binding for the editable properties set as TwoWay. When I edit a value in the details panel, the corresponding value in the data grid is updated. (I don't allow editing directly in the grid.) I can move to another record, then return to record I changed and I can see that the value has successfully been changed (on the client side, at least).
I then added RIA Services to the mix so that I am now retrieving the data from the back end. The data loads fine, but when I try to modify the value of a property in the details panel, it doesn't "stick". That is, the value in the data grid doesn't update to reflect the new value and if I move to another record and return to the changed record, the old value is shown.
Since RIA Services is the thing that changed, I'm assuming that that's where the problem lies.
Next step... I placed a break point in the code generated by RIA Services for client-side consumption inside the setter for the value that I'm changing. When the data is first loaded, the code in the generated setter works fine. When I make a change to the property, however, there appears to be a problem.
Here is the generated code in the setter:
set
{
if ((this._quantity != value))
{
this.OnQuantityChanging(value);
this.RaiseDataMemberChanging("Quantity");
this.ValidateProperty("Quantity", value);
this._quantity = value;
this.RaiseDataMemberChanged("Quantity");
this.OnQuantityChanged();
}
}
After changing the data, I step through the above code in the debugger. When I execute the "RaiseDataMemberChanging..." line, the setter is exited and the rest of the statements are not executed. No exception appears to be thrown and the app continues, but the value isn't updated because the line of code in the setter that sets actually sets the value doesn't appear to be executed.
Any ideas on what the problem is (or at least what I should try next)??
Here is some additional information that may be helpful:
The editing happens in a text box in the details panel. Here's the Xaml for the text box:
<TextBox Grid.Row="2" Grid.Column="0" Text="{Binding Quantity, Mode=TwoWay}" />
Here's the binding used in the data grid. (I don't allow editing there. I set IsReadOnly="True" for the data grid.):
<data:DataGridTextColumn Binding="{Binding Quantity}" Header="Quantity" />
These are both unchanged from when I was binding to the mocked data. The code-behind had to change in order to bind to the different data source. Here's the binding code from the mocked data version:
InitializeComponent();
_industrialDetailsView = new PagedCollectionView((IEnumerable)IndustrialDetailsData.DataSource);
grid.ItemsSource = _industrialDetailsView;
And here's the binding code that uses RIA Services. Note that there is an IndustrialDetailsService on the server side from which the IndustrialDetailsContext is generated by RIA Services.
_industrialDetailsContext = new IndustrialDetailsContext();
_industrialDetailsContext.Load<IndustrialDetailDto>(_industrialDetailsContext.GetByFacilityAndAssessmentYearQuery(202, 2009),
loadOperation =>
{
_industrialDetailsView = new PagedCollectionView(loadOperation.Entities);
grid.ItemsSource = _industrialDetailsView;
}, null);The data context for the details panel has not changed. It is set from the data grid's SelectionChanged event handler like so:
IndustrialDetailDto industrialDetails = (IndustrialDetailDto)grid.SelectedItem;
DetailsView.DataContext = industrialDetails;
Please let me know if there is other information that would be helpful.
I finally determined the nature of the problem when I made the datagrid editable and tried changing the value directly in the datagrid. When I did that, I got an error message saying:
"This EntitySet of type 'X' does not support the 'Edit' operation."
I was then able to solve the problem by adding placeholder methods to the domain service for "Delete", "Insert" and "Update" so that the generated EntitySet allows editing.
Nevertheless, I still think this is a problem that needs to be addressed by Microsoft... I should have received an error message when I tried to modify the text box instead of the generated code simply exiting the setter prematurely.
I've got a datagrid column as below:
<toolkit:DataGridTemplateColumn>
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=LabelName}" Background="{Binding Path=Color}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem x:Name="Assign" Header="Assign"
mvvm:CommandBehavior.Event="Click"
mvvm:CommandBehavior.Command="{Binding Path=DataContext.EditLabelCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}, AncestorLevel='1'}}"
mvvm:CommandBehavior.CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=DataContext}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
I'm trying to get to the DataContext's EditLabelCommand, but I cannot find the binding source. What should I do to access my DataContext's commands?
DataContext changes as you move down the visual tree when you either a) bind DataContext to something else, or b) use something like an items control (or in your case a grid), which repeats your data template once per item in a collection, setting the data context for each item.
In your example, your menu item has the same DataContext as your TextBlock. This will be the object bound to each row of your data grid.
Based on your code, I think you have one EditLabelCommand available in the DataContext set for your entire UserControl. This is likely the parent of the collection you are binding to the grid. (Please correct me if any of these assumptions are wrong.)
If this is the case, there are several things you can do:
You can continue to use relative binding. This is complex and error
prone, as you have seen, and does not lead to good reuse of your data
templates or other XAML. I recommend you avoid this technique.
A simpler way to keep the command in the overall datacontext is to use
a CommandReference from the WPF Model-View-ViewModel Toolkit. This
allows you to reference the command as a resource and use resource binding to
access it. Like this:
<UserControl.Resources>
<mvvmToolkit:CommandReference x:Key="EditLabelCommandReference" Command="{Binding EditLabelCommand}" />
</UserControl.Resources>
<!-- Your command binding then looks much simpler -->
mvvm:CommandBehavior.Command="{StaticResource EditLabelCommandReference}"
Another (probably better) technique is to refactor to use the MVVM pattern for each row. This way, you would use a simple ViewModel per row of your grid. This allows each row to maintain its own state. Because you can move the Command implementation into this ViewModel, you have a command per row and no need to pass an argument. This makes it possible to edit more than one row's label at a time. (If this a requirement.)