Binding to observable property does not work when I try to create my own custom behavior. Neither it does in any of community mvvm toolkit platform behaviors:
https://github.com/CommunityToolkit/Maui/tree/main/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors
Take for example StatusBarBehavior, write something like
<ContentPage.Behaviors>
<toolkit:StatusBarBehavior StatusBarColor="{Binding StatusBarColorProp}" StatusBarStyle="LightContent" />
</ContentPage.Behaviors>
create the property in your view model
[ObservableProperty]
private Color _statusBarColorProp;
you'll see status bar color does not change with StatusBarBehavior property change in runtime. Same for all the rest behaviors. It works fine for non-bindings setters like StatusBarColor="Red".
I wonder if it's a feature or a bug, or I'm missing something.
UPDATE the issue is reported https://github.com/dotnet/maui/issues/11729
I can replicate your issue. And it turns out that we can only change the color of the StatusBarColor either in code behind like below or non-bindings setters like StatusBarColor="Red" as you mentioned.
<ContentPage.Behaviors>
<toolkit:StatusBarBehavior x:Name="statusBar" ></toolkit:StatusBarBehavior>
</ContentPage.Behaviors>
private void OnCounterClicked(object sender, EventArgs e)
{
statusBar.StatusBarColor = Colors.Red;
}
It fails to change the color of StatusBarColor when binding to an observable property. This could be a potential issue and I would suggest that you can raise a Bug Report in Github.
Please file a bug issue at github maui issues.
In the meantime, try this gross hack in one of your properties:
static public BindableProperty MyValueProperty = BindableProperty.Create(...,
propertyChanged: (bindable, oldValue, newValue) =>
{
var it = (MyClass)bindable;
// If it is changing, force explicit OnPropertyChanged. This is usually redundant,
// but might help a binding "cascade" to dependencies.
if (!(MyType)newValue.Equals((MyType)oldvalue))
it.OnPropertyChanged(nameof(MyValue));
});
public MyType MyValue
{
get => (MyType)GetValue(MyValueProperty);
set => SetValue(MyValueProperty, value);
}
Related
I have a UWP Application using ReactiveUI. I navigate to a page with this code:
Router.Navigate.Execute(new AccountListViewModel(this));
The navigation is done. But the ViewModel I created for the navigation is not assigned to my ViewModel in the View. IViewFor<> is implemented as follows:
public sealed partial class AccountListView : IViewFor<AccountListViewModel>
{
public static readonly DependencyProperty ViewModelProperty = DependencyProperty
.Register(nameof(ViewModel), typeof(AccountListViewModel), typeof(AccountListView), null);
public AccountListView()
{
this.InitializeComponent();
this.WhenActivated(disposables =>
{
// My Bindings
...
});
}
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (AccountListViewModel) value;
}
public AccountListViewModel ViewModel {
get => (AccountListViewModel)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
Or do I get something completly wrong here?
According to ReactiveUI RoutedViewHost implementation for Windows, which is used for Universal Windows Platform and for Windows Presentation Foundation, the view model should definitely get assigned to the IViewFor.ViewModel property. You can track changes in the IScreen.Router.CurrentViewModel property to make sure it changes.
If it does, make sure you properly bind your IScreen.Router property to the Router property of the UWP-specific RoutedViewHost XAML control, and routing should finally work. In fact, I tested that behavior on UWP recently and it worked fine for me with ReactiveUI 9.13.1 and latest UWP SDK. Try following the routing tutorial to fully understand how routing works. If this still won't work for you, then uploading a minimal repro that compiles to GitHub could help us understand your issue better. Also, come join ReactiveUI Slack, we are always ready to help out.
I have seen this coding style:
public CustomTextCell()
{
}
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create(
"IsChecked", typeof(bool), typeof(CustomTextCell),
defaultValue: false);
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
}
and this:
public class ExtViewCell : ViewCell
{
public bool NoTap { get; set; }
}
Can someone help explain the difference. Is one serving a different function from the other? In my case all I need is to pass to a custom renderer the value of NoTap. Should I code it like in the first or second example?
The second one is a POCO - a plain old C# object - that is relatively self-explanatory, but serves not much more purpose that holding data - and not that much in this case.
The first one is a bit more interesting, especially in the context of MVVM. SetValue does more than just setting the value, but will (in most cases) raise PropertyChanged event (see INotifyPropertyChanged), to notify subscribers that, well, a property has changed.
Now how does this relate to your custom renderer? You could implement the property in your view as a plain property - i.e. without notifications - and it might work (cannot tell, though, since I do not know your custom renderer) when setting IsChecked initially (and without binding). Anyway, imagine you'll update the value of IsChecked. You do so from your code and wonder, why this change is not reflected in your custom renderer. But how is your renderer supposed to know? Polling each and every property might be possible for smaller forms, but is a terrible waste of resources. (And Xamarin.Forms just does not work this way.) You'll page/view has to tell your custom renderer, that something has changed. INotifyPropertyChanged to the rescue. In your custom renderer you can subscribe to PropertyChanged event and react to IsChecked being changed, updating your native view.
I read this https://bugs.openjdk.java.net/browse/JDK-8102128 but I haven't found something in the Api of JavaFX 8.There isn't any way of prevent reordering in TableView?
Spent half of a day trying to solve the problem. Maybe my investigation can be useful for someone else.
It's possible to disable column reordering using some hacks. And here're the steps:
Get table header. This can be done as described here: https://stackoverflow.com/a/12465155
Add change event listener to reorderingProperty which sets reordering back to false.
Full code is here:
tableView.widthProperty().addListener(new ChangeListener<Number>()
{
#Override
public void changed(ObservableValue<? extends Number> source, Number oldWidth, Number newWidth)
{
TableHeaderRow header = (TableHeaderRow) tableView.lookup("TableHeaderRow");
header.reorderingProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
header.setReordering(false);
}
});
}
});
I'm not sure about side effects of this solution, but quick tests show that solution works well.
I know the question tagged java-8 but for those who wander along, In Java 9 all above codes would break because of modularity which make .sun package inaccessible and removal of impl_. Despite these changes, it introduces convenient public methods that you can use which are:
setReorderable(boolean value)
getReorderable()
for TableColumnBase such as TableColumn to be used for set Reorderable,
I like the idea from Alexander Chingarev but I think the code will produce memory leaks! Every time the width property changes a new listener is registered and older listeners are never garbage collected!
So you can either store a reference to the listener and make sure to remove it from the observable value (in this case the reordering property) before adding a new listener or you make sure the listener is only added once to the reordering property.
I used the skin property which only changes once (if I'm not mistaken) to add my listener:
tableView.skinProperty().addListener((obs, oldSkin, newSkin) -> {
final TableHeaderRow header = (TableHeaderRow) lookup("TableHeaderRow");
header.reorderingProperty().addListener((o, oldVal, newVal) -> header.setReordering(false));
});
An alternative solution which does not require use of private API in Java 8.
public static <T> void preventColumnReordering(TableView<T> tableView) {
Platform.runLater(() -> {
for (Node header : tableView.lookupAll(".column-header")) {
header.addEventFilter(MouseEvent.MOUSE_DRAGGED, Event::consume);
}
});
}
The API added in Java 8 RT-24669, is column.impl_setReorderable(false);.
You can see the impl_setReorderable definition in the TableColumnBase.java source.
However, this API is only intended for internal use, marked as deprecated and does not form part of the public JavaFX API.
In general, impl_ methods in JavaFX will be removed at some time in the future, potentially breaking your code at that time if you tried to use them.
In reviewing the code for the implementation of the reorderable property on table columns, it works by ignoring certain mouse events directed to the table column header. Search the TableColumnHeader.java code for isReorderable for more info.
I'm not sure how you would accomplish the exact same behaviour as the impl_setReorderable API using only the public JavaFX API without performing quite a lot of work.
Expanding on a previous contribution I found this
tableView.getColumns().forEach(e -> e.setReorderable(false));
to be very useful in Java 13.
Thanks! It works with lambda as well if you have multiple tables you want to disable.
private void yourTableListeners(){
yourTableView.widthProperty()
.addListener((observable, oldValue, newValue) -> yourReusableDisablingMethod(yourTableView));
anotherTableView.widthProperty()
.addListener((observable, oldValue, newValue) -> yourReusableDisablingMethod(anotherTableView));
}
#SuppressWarnings("restriction")
private void yourReusableDisablingMethod(TableView tableView) {
TableHeaderRow header = (TableHeaderRow) tableView.lookup("TableHeaderRow");
header.reorderingProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
header.setReordering(false);
}
});
}
For FXML user it can easily done by adding reorderable="false"
<TableColumn fx:id="test" text="ColumnTitle" reorderable="false"/>
If you down want sorting on the tableview columns and tab reordering there is a simple solution
private void disableHeaderReorderingAndSorting() {
tableView.skinProperty().addListener((a, b, newSkin) ->
{
Pane header = (Pane) tableView.lookup("TableHeaderRow");
header.setMouseTransparent(true);
});
}
Sorry for the vague(ish) title, I'm working on a WPF project, and it's getting rather annoying. I know that the VS designer is a bit finickity at times, but hoping it's something that I can fix.
I've got a dependency property that I'm putting a binding too, however the designer is giving me blue squiggles and an error:
Error 13 A 'Binding' cannot be used within a 'TextBlock' collection. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
However when I run the app, it's all working fine, no binding errors for that, the it all works as expected. VS has been restarted many times since it first happened, and it still occurs.
I can't see anything wrong with the DependancyProperty that it's referring to, all looks pretty standard to me, but maybe one of you guys can shed some light (hopefully). I cannot remember where I got the code for the DP from, I know it was online, but I've tweaked slighty from that (I think).
Running VS2010, project is targeting .net4.0 (not the Client Profile).
Thanks!
XAML
<TextBlock Grid.Column="1" Grid.Row="0" AllowDrop="True" behaviours:DropBehavior.PreviewDropCommand="{Binding Path=DropFile}" Style="{StaticResource styFile}">
DP
public static class DropBehavior {
private static readonly DependencyProperty PreviewDropCommandProperty = DependencyProperty.RegisterAttached(
"PreviewDropCommand",
typeof(ICommand),
typeof(DropBehavior),
new PropertyMetadata(null, PreviewDropCommandPropertyChangedCallBack)
);
public static void SetPreviewDropCommand(this UIElement inUIElement, ICommand inCommand) {
inUIElement.SetValue(PreviewDropCommandProperty, inCommand);
}
private static ICommand GetPreviewDropCommand(UIElement inUIElement) {
return (ICommand)inUIElement.GetValue(PreviewDropCommandProperty);
}
private static void PreviewDropCommandPropertyChangedCallBack(
DependencyObject inDependencyObject, DependencyPropertyChangedEventArgs inEventArgs) {
UIElement uiElement = inDependencyObject as UIElement;
if (null == uiElement)
return;
uiElement.Drop += (sender, args) => {
GetPreviewDropCommand(uiElement).Execute(args.Data);
args.Handled = true;
};
}
}
After much putting up the UI whining about it, took another look at the issue again, turns out it was this line here:
private static readonly DependencyProperty PreviewDropCommandProperty = DependencyProperty.RegisterAttached(
"PreviewDropCommand",
typeof(ICommand),
typeof(DropBehavior),
new PropertyMetadata(null, PreviewDropCommandPropertyChangedCallBack)
);
It should have been a public, not private declaration. Curious that the app runs fine with it, just not the designer (or perhaps not so curious if I knew the inner workings of VS)
I have a property and depending upon it's state (say A and B) I either show a usercontrol of animation or a image.
Now, if the property changes, I want to trigger the datatemplate selector again. On searching, I found that in WPF I could have used DataTemplate.Trigger but it's not available in WP.
So, my question is
Is their a way to trigger datatemplate selector so when property changes from state A to B, then appropriate usercontrol gets selected. If yes, then please give some example how to implement it.
Also, as there are only two states, if think I can use Converter to collapse the visibility. For basic if else situation, I will need to write two converters.( Can I somehow do it using one converter only?)
Here is the exact situation.
If state == A :
select userControl_A
else : select userControl_B
Also,
Will the animation usercontrol will effect the performance when it's in Collapsed state?
EDIT- Just realized, I can use parameter object to write just one converter.
You could implement a DataTemplateSelector like described here.
I use it and it works pretty well.
EDIT:
If you need to update the DataTemplate when the property changes, you should subscribe to the PropertyChanged event of the data object in the TemplateSelector and execute the SelectTemplate method again.
Here is the code sample:
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
City itemAux = item as City;
// Subscribe to the PropertyChanged event
itemAux.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(itemAux_PropertyChanged);
return GetTemplate(itemAux, container);
}
private DataTemplate GetTemplate(City itemAux, DependencyObject container)
{
if (itemAux != null)
{
if (itemAux.Country == "Brazil")
return BrazilTemplate;
if (itemAux.Country == "USA")
return UsaTemplate;
if (itemAux.Country == "England")
return EnglandTemplate;
}
return base.SelectTemplate(itemAux, container);
}
void itemAux_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// A property has changed, we need to reevaluate the template
this.ContentTemplate = GetTemplate(sender as City, this);
}