I have a combo box which contains items of type Dog. If all items are replaced with new ones (via setAll on the ObservableList model) , the item renderer can cope with this update, while the button cell renderer cannot:
Here's a minimal example to reproduce the problem (full source incl. imports on GitHub):
public class ComboBoxRefresh extends Application {
private static final class Dog {
private final String name;
public Dog(String name) {
this.name = name;
}
}
private static final class DogListCell extends ListCell<Dog> {
#Override
public void updateItem(Dog item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
setText(item.name);
}
}
}
private static List<Dog> createThreeDogs() {
return range(0, 3).mapToObj(i -> new Dog("Buddy " + i)).collect(toList());
}
#Override
public void start(Stage stage) throws Exception {
ObservableList<Dog> items = observableArrayList(createThreeDogs());
ComboBox<Dog> comboBox = new ComboBox<>(items);
comboBox.setPrefWidth(400);
comboBox.setCellFactory(listView -> new DogListCell());
comboBox.setButtonCell(new DogListCell());
Button button = new Button("Refresh");
button.setOnAction(event -> {
List<Dog> newItems = createThreeDogs();
items.setAll(newItems);
});
VBox box = new VBox(10, comboBox, button);
box.setPadding(new Insets(10));
Scene scene = new Scene(box);
stage.setScene(scene);
stage.show();
}
}
If I add an equals implementation to the Dog class, everything works, but this is not an option in my real application.
Are there any work-arounds to enforce a proper refresh of the button cell?
It seems to be a bug. Workaround could be
button.setOnAction( event -> {
List<Dog> newItems = createThreeDogs();
items.clear();
items.addAll(newItems);
} );
I'm using GXT 3.0 and have a Grid with multiple checkboxes each row. These checkboxes reflect certain properties of my row data and checkin/unchecking does not implies selecting/unselecting the particular row. How can I add a listener to each checkbox and perform some action upon clicking it?
I override the handlesSelection() method to capture the check/uncheck event
CheckBoxCell checkCol = new CheckBoxCell() {
#Override
public boolean handlesSelection() {
//TODO:
return true;
}
};
Add some CheckBoxSelectionModel for each checkbox
IdentityValueProvider<Stock> identity = new IdentityValueProvider<Stock>();
SpecialRowClickCheckBoxSelectionModel<Stock> sm =
new SpecialRowClickCheckBoxSelectionModel<Stock>(identity);
public class SpecialRowClickCheckBoxSelectionModel<M>
extends CheckBoxSelectionModel<M> {
public SpecialRowClickCheckBoxSelectionModel(
IdentityValueProvider<M> identity) {
super(identity);
}
#Override
protected void handleRowClick(RowClickEvent event) {
M model = listStore.get(event.getRowIndex());
//TODO
}
}
I have a simple implementation of a ListView in WPF that allows me to select multiple items in the list by holding the mouse button and dragging over the items. However, while holding the mouse button down, when I move the mouse outside the ListView, something strange happens with the selection. Ideally, I would just want the selection to remain the same, but instead it quickly cycles through all the selected items, leaving only the last item selected.
Here's the code, have any ideas?
public class MultiSelectListView : ListView
{
private bool m_isSelectionActive;
public bool IsSelectionActive
{
get { return m_isSelectionActive; }
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MultiSelectListViewItem(this);
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
m_isSelectionActive = true;
}
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
m_isSelectionActive = false;
}
}
public class MultiSelectListViewItem : ListViewItem
{
private readonly MultiSelectListView m_parent;
public MultiSelectListViewItem(MultiSelectListView parent)
{
m_parent = parent;
}
protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
{
if (m_parent.IsSelectionActive)
IsSelected = true;
}
}
The funkiness you are experiencing happens when the mouse "drag" goes above the top of the list or below the bottom of the list. I think the behavior you set up will only work well if the selection mode is Multiple. The modifications to the MultiSelectListView below set the default selection mode to Multiple and assumes the user wants to start another selection with a left mouse click. You will still experience funkiness if the SelectionMode is set to Extended or Single in the XAML.
public class MultiSelectListView : ListView
{
private bool m_isSelectionActive;
public bool IsSelectionActive
{
get
{
return m_isSelectionActive;
}
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MultiSelectListViewItem(this);
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (SelectionMode != SelectionMode.Single)
{
SelectedItems.Clear();
}
m_isSelectionActive = true;
}
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
m_isSelectionActive = false;
}
public MultiSelectListView() : base()
{
SelectionMode = SelectionMode.Multiple;
}
}
Are there any known issues when databinding to a control's visible property?
The control is always NOT visible regardless of what my property is.
Public ReadOnly Property IsRibbonCategory() As Boolean
Get
Return True
End Get
End Property
I tried the control's text property and other properties and they seem to work correctly.
I am trying to set a Panel's visible property.
I've found that life is better if you assume that binding to a control's Visible property is broken, despite the fact that it sometimes works. See http://support.microsoft.com/kb/327305, which says as much (and while the KB article applies to .NET 1.0 and 1.1, it still seems to be a problem in at least 2.0).
I created a utility class for creating bindings which, among other things, gave me a centralized place to add a work-around. Instead of actually creating a binding on Visible it does two things:
It subscribes to the data source's INotifyPropertyChanged.PropertyChanged event and sets the Visible value as appropriate when the event is raised.
It sets the initial value of Visible according to the current data source value.
This required a little reflection code, but wasn't too bad. It is critical that you don't bind the Visible property and do the work-around or it won't work.
Workaround: Set the Visible property on the BindingComplete event.
I had same issue setting a label's Visible property - always stays false, even though setting the Enabled property works fine.
I just hit this issue in .NET 4.7.1 and Visual Studio 2017. To fix it, I changed the Visible property on my control to be initially set to True, as I had it as False previously.
Things to check:
Be sure you've instantiated the class that has the IsRibbonCategory property
Did you set the datasource of property of the binding source to the instance of the class
The datasource update mode should be on "on validation"
Make sure you didn't set the visible property manually to false on the control
Hope that helps. Can you post more code?
A workaround would be to use a Component to databind to a control's visiblity property instead of directly binding to the control's visibility property.
See below code:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public class ControlVisibilityBinding : Component
{
private static readonly object EventControlChanged = new object();
private static readonly object EventVisibleChanged = new object();
private System.Windows.Forms.Control _control;
private bool _visible = true;
public event EventHandler VisibleChanged
{
add { Events.AddHandler(EventVisibleChanged, value); }
remove { Events.RemoveHandler(EventVisibleChanged, value); }
}
public event EventHandler ControlChanged
{
add { Events.AddHandler(EventControlChanged, value); }
remove { Events.RemoveHandler(EventControlChanged, value); }
}
public ControlVisibilityBinding()
{
}
public ControlVisibilityBinding(IContainer container)
{
container.Add(this);
}
[DefaultValue(null)]
public System.Windows.Forms.Control Control
{
get { return _control; }
set
{
if(_control == value)
{
return;
}
WireControl(_control, false);
_control = value;
if(_control != null)
{
_control.Visible = _visible;
}
WireControl(_control, true);
OnControlChanged(EventArgs.Empty);
OnVisibleChanged(EventArgs.Empty);
}
}
[DefaultValue(true)]
public bool Visible
{
get { return _visible; }
set
{
if(_visible != value)
{
_visible = value;
}
if(Control != null)
{
Control.Visible = _visible;
}
OnVisibleChanged(EventArgs.Empty);
}
}
private void WireControl(Control control, bool subscribe)
{
if(control == null)
{
return;
}
if(subscribe)
{
control.VisibleChanged += Control_VisibleChanged;
}
else
{
control.VisibleChanged -= Control_VisibleChanged;
}
}
private void Control_VisibleChanged(object sender, EventArgs e)
{
OnVisibleChanged(EventArgs.Empty);
}
protected virtual void OnVisibleChanged(EventArgs e)
{
EventHandler subscribers = (EventHandler)Events[EventVisibleChanged];
if(subscribers != null)
{
subscribers(this, e);
}
}
protected virtual void OnControlChanged(EventArgs e)
{
EventHandler subscribers = (EventHandler)Events[EventControlChanged];
if(subscribers != null)
{
subscribers(this, e);
}
}
}
static class Program
{
[STAThread]
static void Main()
{
using(Form form = new Form())
using(FlowLayoutPanel groupBoxLayoutPanel = new FlowLayoutPanel())
using(RadioButton visibleButton = new RadioButton())
using(RadioButton hiddenButton = new RadioButton())
using(GroupBox groupBox = new GroupBox())
using(Label text = new Label())
using(ControlVisibilityBinding visibilityBinding = new ControlVisibilityBinding())
using(TextBox inputTextBox = new TextBox())
{
groupBoxLayoutPanel.Dock = DockStyle.Fill;
groupBoxLayoutPanel.FlowDirection = FlowDirection.LeftToRight;
groupBoxLayoutPanel.AutoSize = true;
groupBoxLayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
visibleButton.Text = "Show Label";
visibleButton.AutoSize = true;
hiddenButton.Text = "Hide Label";
hiddenButton.AutoSize = true;
groupBoxLayoutPanel.Controls.Add(visibleButton);
groupBoxLayoutPanel.Controls.Add(hiddenButton);
inputTextBox.Text = "Enter Label Text Here";
inputTextBox.Dock = DockStyle.Top;
groupBox.AutoSize = true;
groupBox.AutoSizeMode = AutoSizeMode.GrowAndShrink;
groupBox.Controls.Add(groupBoxLayoutPanel);
groupBox.Dock = DockStyle.Fill;
text.AutoSize = true;
text.ForeColor = Color.Red;
text.Dock = DockStyle.Bottom;
text.BorderStyle = BorderStyle.FixedSingle;
text.Font = new Font(text.Font.FontFamily, text.Font.Size * 1.25f, FontStyle.Bold | FontStyle.Italic);
text.DataBindings.Add("Text", inputTextBox, "Text", true, DataSourceUpdateMode.Never);
visibilityBinding.Control = text;
visibleButton.DataBindings.Add("Checked", visibilityBinding, "Visible", true, DataSourceUpdateMode.OnPropertyChanged);
Binding binding = hiddenButton.DataBindings.Add("Checked", visibilityBinding, "Visible", true, DataSourceUpdateMode.OnPropertyChanged);
ConvertEventHandler invertConverter = (sender, e) => e.Value = !((bool)e.Value);
binding.Format += invertConverter;
binding.Parse += invertConverter;
form.Controls.Add(inputTextBox);
form.Controls.Add(text);
form.Controls.Add(groupBox);
Application.Run(form);
}
}
}
}
Here is my turn around, it may be stupid but it worked many times.
I put one Panel control in my form, I make it to Fill my form and I put everything in that Panel. All the controls I bind the Visible property see their visibility change according to the objects in my DataGridView.
I have a Silverlight DataGrid of which I need to check if it has Focus. I know there is a method to set Focus and an event for GotFocus but can't see anyhting for checking if it has focus.
Any Ideas ?
AFAIK there is no direct method or property to check if it has focus, but you should be able to use the FocusManager.GetFocusedElement().
If you then define a extension method, you should be able to call MyDataGrid.HasFocus():
public static class ControlExtensions
{
public static bool HasFocus(this Control aControl)
{
return System.Windows.Input.FocusManager.GetFocusedElement() == aControl;
}
}
[edited: I did test it now:]
However there is catch: the call GetFocusedElement() can return the current focused cell within the DataGrid. So in that case the HasFocus will return false.
To be able to check if the DataGrid or one of its cells are focused, we can adapt our extension method like this
public static class ControlExtensions
{
public static bool HasFocus(this Control aControl, bool aCheckChildren)
{
var oFocused = System.Windows.Input.FocusManager.GetFocusedElement() as DependencyObject;
if (!aCheckChildren)
return oFocused == aControl;
while (oFocused != null)
{
if (oFocused == aControl)
return true;
oFocused = System.Windows.Media.VisualTreeHelper.GetParent(oFocused);
}
return false;
}
}
Hope this helps a bit?