as it said, how can I change item decoration in recyclerview when size changed? I've already tried invalidate(), but nothing changed.So is there some sort of methods like "notifydataset"?
the adapter will be a good candidate to handle the events.
If you want to add or remove items from the adapter, you will need to explicitly inform to the adapter. That’s a bit different from the former notifyDataSetChanged():
public void add(ViewModel item, int position) {
items.add(position, item);
notifyItemInserted(position);
}
public void remove(ViewModel item) {
int position = items.indexOf(item);
items.remove(position);
notifyItemRemoved(position);
}
Related
I'm making a custom TextBox for UWP to simplify Win2D outlined text solution, for that I created a UserControl that contains only a canvas on which I'll draw the text.
Of course I need some properties, like text, outline thickness and color, etc...
I also need some properties that are already exposed by the inherited UserControl like Foreground, FontSize, FontFamily...
So far so good, it seems like I won't need to implement each one of those common properties.
The problem is that I can't find a way to hook up an event when one of those properties changes, as I have to call the Canvas.Invalidate() method to redraw it when the format changes.
Looks like I have to hide all those properties and create new Dependency Properties to call Canvas.Invalidate().
There is no way to do it faster?
Nevermind, the answer was behind the corner.
In the constructor, you can call
RegisterPropertyChangedCallback(DependencyProperty dp, DependencyPropertyChangedCallback callback);
For example:
public OutlinedText()
{
InitializeComponent();
RegisterPropertyChangedCallback(FontFamilyProperty, OnPropertyChanged);
RegisterPropertyChangedCallback(FontSizeProperty, OnPropertyChanged);
}
private void OnPropertyChanged(DependencyObject sender, DependencyProperty dp)
{
OutlinedText instance = sender as OutlinedText;
if (instance != null)
{
//Caching the value into CanvasTextFormat for faster drawn execution
if (dp == FontFamilyProperty)
instance.TextFormat.FontFamily = instance.FontFamily.Source;
else if (dp == FontSizeProperty)
instance.TextFormat.FontSize = (Single)instance.FontSize;
instance.needsResourceRecreation = true;
instance.canvas.Invalidate();
}
}
I have a UWP application which uses the Managed UWP Behavior SDK.
I wrote a custom behavior which has two dependency properties, one of which is a ObservableCollection.
Whenever I update an item in the collection, I make sure that PropertyChanged is called for the collection.
However, the Dependency property is not being updated.
My code:
<trigger:CustomBehavior ItemIndex="{x:Bind ItemIndex}"
Presences="{Binding ElementName=Box,
Path=DataContext.CustomCollection,
UpdateSourceTrigger=PropertyChanged, Converter={StaticResource TestConverter}}" />
My TestConverter shows me that when I update an item in the collection, the updatesource trigger is working. The dependency property in my behavior however, is not firing the Changed event. When I change the entire custom collection, the DP is updated, when I just change one item, it isn't.
Research so far says that DependencyObject.SetValue just checks to see if the object has changed and if one item changed, it will just think that the collection didn't change at all? Is this true, and if so, how can I overcome this?
Thanks
A collection-type dependency property should usually be declared as the most basic collection type, IEnumerable. This way you can assign a variety of actual collection types to the property, including those that implement INotifyCollectionChanged, like ObservableCollection<T>.
You would check at runtime if the collection type actually implements the interface, and possibly attach and detach a handler method for the CollectionChanged event.
public class CustomBehavior : ...
{
public static readonly DependencyProperty PresencesProperty =
DependencyProperty.Register(
"Presences", typeof(IEnumerable), typeof(CustomBehavior),
new PropertyMetadata(null,
(o, e) => ((CustomBehavior)o).OnPresencesPropertyChanged(e)));
private void OnPresencesPropertyChanged(DependencyPropertyChangedEventArgs e)
{
var oldCollectionChanged = e.OldValue as INotifyCollectionChanged;
var newCollectionChanged = e.NewValue as INotifyCollectionChanged;
if (oldCollectionChanged != null)
{
oldCollectionChanged.CollectionChanged -= OnPresencesCollectionChanged;
}
if (newCollectionChanged != null)
{
newCollectionChanged.CollectionChanged += OnPresencesCollectionChanged;
// in addition to adding a CollectionChanged handler, any
// already existing collection elements should be processed here
}
}
private void OnPresencesCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
// handle collection changes here
}
}
In a my view I have an HBox
#FXML
private HBox hboxWarning;
and I want hide/show it according to the value of
private ObjectProperty<Integer> maxClientCount;
If maxClientCount > 10 then hboxWarning is visible else it's hide.
I bound the two elements in this way
hboxWarning.visibleProperty().bind(IntegerProperty.integerProperty(maxClientCount).greaterThan(10));
and works well. My problem is that
IntegerProperty.integerProperty(maxClientCount)
sets to zero the current value of maxClientCount. Is it a JavaFx bug or I'm using IntegerProperty.integerProperty improperly? And
how can I achieve my goal?
Turned out to be not as easy as assumed: the core fix needs additional methods in BidirectionalBinding to cope with the swapped sequence of number types. The actual number bindings are private, so no way to access in workaround code.
// method in u5, binds the wrong way round
// (for usage in IntegerProperty.integerProperty)
public static BidirectionalBinding bindNumber(Property<Integer> property1,
IntegerProperty property2)
// calls
private static <T extends Number> BidirectionalBinding bindNumber(Property<T> property1,
Property<Number> property2) {
The sequence is crucial because we need a type-cast from Number to T when setting the value of p1 (which is safe because we know that the number-type property copes with conversion from Number -> concrete type). Core fix simply adds all those methods with switched parameter sequence.
For a custom hack until the release of JDK 8u20, the only way I see is to not use the special number binding methods but the generic object binding:
public static IntegerProperty integerProperty(final Property<Integer> property) {
if (property == null) {
throw new NullPointerException("Property cannot be null");
}
return new IntegerPropertyBase() {
{
bindBidirectional(cast(property));
// original:
//BidirectionalBinding.bindNumber(property, this);
}
#Override
public Object getBean() {
return null; // Virtual property, no bean
}
#Override
public String getName() {
return property.getName();
}
#Override
protected void finalize() throws Throwable {
try {
unbindBidirectional(cast(property));
// original
// BidirectionalBinding.unbindNumber(property, this);
} finally {
super.finalize();
}
}
};
}
/**
* Type cast to allow bidi binding with a concrete XXProperty (with
* XX = Integer, Double ...). This is (?) safe because the XXProperty
* internally copes with type conversions from Number to the concrete
* type on setting its own value and exports the concrete type as
* needed by the object property.
*
*/
private static <T extends Number> Property<Number> cast(Property<T> p) {
return (Property<Number>) p;
}
Take it with a grain of salt - while rudimentarily tested, there might be side-effects I overlooked.
As rightly said by #kleopatra this is a JavaFx bug fixed in JDK 8u20.
Meanwhile I used the following workaround:
int maxClients = maxClientCount.get();
hboxWarning.visibleProperty().bind(IntegerProperty.integerProperty(maxClientCount).greaterThan(10));
maxClientCount.setValue(maxClients);
I hope this can help someone.
I'm working on a Windows 8 Store App (using the Grid App Template) and while I'm loading data from a server I want to show a ProgressRing and hide the GridView or ListView (depends on if the app is snapped or not) that will display the data once it is fully loaded.
The issue is that when the ViewModel is loading data I need to be able to change the VisualState.
I found what I thought was a solution Here, but this code will not build.
public class StateManager : DependencyObject
{
public static string GetVisualStateProperty(DependencyObject obj)
{
return (string)obj.GetValue(VisualStatePropertyProperty);
}
public static void SetVisualStateProperty(DependencyObject obj, string value)
{
obj.SetValue(VisualStatePropertyProperty, value);
}
public static readonly DependencyProperty VisualStatePropertyProperty =
DependencyProperty.RegisterAttached(
"VisualStateProperty",
typeof(string),
typeof(StateManager),
new PropertyMetadata((s, e) => //this throws the error
{
var propertyName = (string)e.NewValue;
var ctrl = s as Control;
if (ctrl == null)
throw new InvalidOperationException("This attached property only supports types derived from Control.");
System.Windows.VisualStateManager.GoToState(ctrl, (string)e.NewValue, true);
}));
}
ERROR: Cannot convert lambda expression to type 'object' because it is
not a delegate type
Does anyone know how to get the linked solution to work? Or is there a simpler method that I am completely missing (I'm a XAML newbie!)?
I'm not even sure if the listed solution will work because the "Snapped" vs "Full" states are managed by the base LayoutAwarePage class included with the template.
why not simply use a datatrigger bind to a viewmodel property like IsBusy {get;set;} to enable your Progressring?
I have a list of items that are displayed in portrait orientation in a wp8 app. I have defined GroupHeaderTemplate and JumpListStyle, so it neatly shows as a grouped list. I created a KeyedList class that represents a list grouped by a key and my ViewModel exposes ObservableCollection of KeyedList.
using System.Collections.Generic;
using System.Linq;
namespace Timbre.Collections
{
public class KeyedList<TKey, TItem> : List<TItem>
{
public TKey Key
{
get;
protected set;
}
public KeyedList(TKey key)
: base()
{
Key = key;
}
public KeyedList(TKey key, IEnumerable<TItem> items)
: base(items)
{
Key = key;
}
public KeyedList(IGrouping<TKey, TItem> grouping) :
base(grouping)
{
Key = grouping.Key;
}
}
}
The Property in the ViewModel is as follows:
public ObservableCollection<KeyedList<string, Album>> Albums
{
get { return this.groupedAlbums; }
private set
{
if (this.groupedAlbums == value) return;
this.groupedAlbums = value;
RaisePropertyChanged(() => this.Albums);
}
}
The LongListSelector binds to this ObservableCollection and works properly.
Now when the orientation of the page changes to landscape, I want to change the layout mode from List to Grid on the LongListSelection and I want to get rid of the grouping as it takes quite some space and I just want to display a grid of pictures.
Typically I would set this on the long list when orientation changes to landscape:
IsGroupingEnabled=false
But I'm no longer able to bind to the same ObservableCollection I used before, as it exposes a list of lists and not a simple list. Now I know this can be solved by exposing 2 ObservableCollections in the ViewModel, one for grouped list and one for simple list. But typically, I want to show the same item that was visible to the user in the flat list when the layout mode changes to grid. Is there some way to just expose one ObservableCollection and yet just change the LayoutMode from List to Grid and make it work?