Getting parent in XAML - xaml

I have following XAML code :
<maps:Map Name="PlaygroundMap" Credentials="YOUR_BING_MAPS_KEY">
<maps:MapItemsControl
x:Name="PlaygroundMapItems"
my:MapExt.ClusteredItemsSource="{Binding Locations}"
ItemTemplate="{StaticResource PinTemplate}"/>
</maps:Map>
Also ClusteredItemsSource is my own attached property with the following code (only important part is shown) :
private static void OnItemsReceived(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var mapItemsControl = d as MapItemsControl;
if(mapItemsControl == null) return;
var map = VisualTreeHelper.GetParent(mapItemsControl); // this is always NULL
var map2 = mapItemsControl.Parent; // this is always NULL
var items = e.NewValue as IEnumerable<Location>;
mapItemsControl.ItemsSource = items;
}
So my requirement is to have MAP reference in OnItemsReceived method. I thought that this will be easy because if you check XAML again you will see that mapItemsControl is child of Map (or map is parent of mapItemsControl).
But somehow variable map & map2 are always NULL. Is this normal behaviour or?
Also, if this "parent" approach will not work, can you suggest me what are my other options to get Map reference in MapItemsControl (to be exact, to get map reference in my custom attached property).
Btw, I'm using Bing Maps SDK for WinRT.

VisualTreeHelper won't help you if your controls are not part of the visual tree - either because they were not added to one yet (Loaded event wasn't raised yet) or maybe they don't use the standard parent/child control relationship triggers (by one being logically a child of a Panel, ContentControl etc.)
The best approach here really is to forget about the visual tree and use bindings and view models instead.

Related

Jetpack compose not recomposing view on value change

I am using objectbox and attempting to force recomp on my lazy column with information from the viewModel.
I currently use a method that has been state hoisted to the view model but can't seem to retrieve the value in the view model and force recomp.
In my view model (DataFieldsViewModel), I've obtained all the Data Fields like this:
private val _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java)
var dataFieldsBox: Box<DataField> = _dataFieldsBox
and then it is passed in the composable screen using
fields = viewModel.dataFieldsBox
and the data fields object removed with
is DataFieldEvent.ConfirmDelete -> {
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(deletedDataField.value)
}
the reference for deletedDataField is kept so that it can be restored from a snack bar.
The delete works, the restore works, it's just that the lazy column does not update until I click another view that forces recomp. I've even mocked up a simple button with state hoisted increment and decrement methods and a value in the view model to test out recomp and got that working pretty quick, just don't know how to do it for these ObjectBox DataField objects
Edit 18/08
Tried using mutableStateOf
private var _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java)
var dataFieldsBox = mutableStateOf(_dataFieldsBox)
and then attempting to collect the value with
val fields by viewModel.dataFieldsBox
and then deleting with
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(event.dataField)
only fix right now is the two hacky ways which i don't like. I read something about despite the values in a mutable list changing because the reference is the same the recomp doesn't happen to save on recomps.
So tried to amend the list and then duplicate with new values which also did nothing.
is DataFieldEvent.ConfirmDelete -> {
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(event.dataField)
val newList = _dataFieldsBox
dataFieldsBox = mutableStateOf(newList)
}
Would be helpful if you update viewmodel code you are having.
Not worked on ObjectBox. With your current code, here is the thing.
Composable function will recompose when it is able to listen to changes. For normal variable it does not listen to changes. You can make them listen in several ways.
You can do it with mutableStateOf(). You can have mutableState variable which takes type of _dataFieldsBox . And so can access it by viewModel._dataFieldsBox.value.
private val _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java
var _dataFieldsBox = mutableStateOf(_dataFieldsBox)
And then in view model,
_dataFieldsBox.value.remove(_dataFieldsBox) // This will remove the value in the state variable and composable function will recompose.
You can also use LiveData and observe for the changes and it will recompose.

Accessing Resources IDs using Kotlin & Anko

I am new to Android/Kotlin/Anko and I have a question regarding the way to access color (and probably other) resources from within Anko.
I know that there are helpers like textResource where you simply pass the R.string.my_color to simplify the process of setting resource strings but how about accessing colors using the Resources instance from the View class ?
Let’s say you have a subclass of Button and want to change the text color. If you use the textResource it will change the text string not the color, and if you use textColor then you must specify the real resource ID by using resources.getColor(R.color.my_color, null) which wouldn't be so annoying if you didn't have to pass the optional theme parameter (null here)
Is creating an extension on Resources useful here ?
fun Int.fromResources(resources: Resources): Int {
return resources.getColor(this, null)
}
What is the recommended way ?
EDIT
I changed the textColor value extension to do just that, which I found the cleanest thing to do except I have no idea if this is really Android friendly
var android.widget.TextView.textColor: Int
get() = throw AnkoException("'android.widget.TextView.textColor' property does not have a getter")
set(v) = setTextColor(resources.getColor(v, null))
I think you can use a property extension like this one instead of the one you suggested:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(resources.getColor(v, null))
Or use ContextCompat as suggested by Damian Petla:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(ContextCompat.getColor(context, v))
You should keep Anko's textColor:
Allows you to set a color directly without taking it from XML, if needed at some point
Prevents you from importing the wrong textColor (Anko's one or yours), same property names with different behaviour is not a good idea.

JavaFX Check Cell background of specific Cell (random access)

I just started to develop a JavaFX application. Maybe I didn't get how JavaFX uses the TableView and I should use something different instead.
Currently my TableView displays data in multiple columns an when I double-click a cell the background color changes (by setCellFactory(customFactory)).
Now I want to access different cells of the table by using indices (column,row) and checking the background color.
The cells with a changed background color should be stored after a certain button was clicked.
I would like to get every cell with changed background(get celltext) for each row and store this for later use in a data structure like a Map>.
Would be really nice if somebody can give me a hint. Thank for your Help.
I suppose, you are adding an EventHandler to the TableCell, which is returned by your customFactory. This EventHandler is handling the doubleclick-event and sets the background color, right?
This handler has access to the parameter which is passed to the Callbacks/CustomFactories call-method, which contains the model-bean of the current row. You could set a flag or the columns name in that model-bean when a doubleClickEvent occurs.
Then
after a certain button was clicked
you can get your info, by checking the tables items. The row-index of each item is equivalent to the index of this item in the List of TableView#getItems
Also have a look at http://controlsfx.bitbucket.org/org/controlsfx/control/spreadsheet/SpreadsheetView.html if you need more TableFunctions.
EDITED
This is a code-example:
The Model-Bean used in TableView:
class Model {
private String propertyA;
private String propertyB;
#lombok.Getter
private Set<String> propertiesClicked = new HashSet<>();
The javafx-controls, annotate them with #FXML if you use FXMLs:
private TableView<Model> tableView;
private TableColumn<Model, String> propertyAColumn;
private TableColumn<Model, String> propertyBColumn;
and the the CellFactory. Create a more generic CellFactory if you need it for multiple columns:
propertyAColumn.setCellFactory((value) -> {
TableCell<Model, String> tableCell = new TableCell<Model, String>() {
//Override the Methods which you need
};
tableCell.setOnMouseClicked((mouseEvent) -> {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
if (mouseEvent.getClickCount() == 2 && !tableCell.getStyleClass().contains("buttonClicked")) {
tableCell.getStyleClass().add("buttonClicked");
tableView.getSelectionModel().getSelectedItem().getPropertiesClicked().add("propertyA");
}
}
});
return tableCell;
});

Binding IEnumerable<anonymous object> to DataGrid in WPF

I need merely a readonly display of Linq data; no requirement to observe changes. Here's the Linq:
string ZIPCODEFIELDNAME="zip5";
DataTable DATA = (detailedQueryResult as DataTable);
IEnumerable<object> ZIPCODE_SUMMARY;
ZIPCODE_SUMMARY = from z in DATA.AsEnumerable()
group z by z.Field<string>(ZIPCODEFIELDNAME) into g
let list = g.ToList()
select new
{
zip = g.Key,
eecount = list.Count(),
// possible additional aggregate columns here
};
I am able to bind this IEnumerable<anonymous object> to a Telerik RadGridView in code-behind simply by doing this:
myRadGridView.ItemsSource = ZIPCODE_SUMMARY.ToList();
that is, without having to declare a binding in XAML or having to define any columns. How would that be accomplished using the WPF DataGrid that ships with Visual Studio? It displays only row separators when I use the identical approach. So it knows about the items in the list, just not how to fetch the columns.
I am trying to write some quick-and-dirty utilities to import a gazillion CSV files where no two of them have the same structure or same field names, and the fewer lines of setup code the better.
P.S. I am just getting into WPF and Linq, so please set ReplyToNovice=true :-)
Did you remember to set AutoGenerateColumns=true on the datagrid? If yes, try binding to an ICollectionView instead.
UPDATE:
Thats weird, the code below works fine for me. One thing though, you may have to set the datacontext of the datagrid to {Binding}, this will bind to the whole object.
public MainWindow()
{
InitializeComponent();
dgZips.ItemsSource = GetFakeZips();
}
public dynamic GetFakeZips()
{
return Enumerable.Range(1500, 10).Select(i => new { Zip = i, Count = i / 4 });
}
Xaml:
<DataGrid x:Name="dgZips" AutoGenerateColumns="True" />

cellBrowser placed in composite - do I have to attach directly to the rootpanel (or rootlayoutpanel)

when using a cellbrowser and adding that widget to a flowpanel (to be placed wherever, downstream), for some reason the end result is dead (a blank screen)...vs if I add directly to the rootpanel (or layout panel)
Also had the same problem. I had to use a <g:HTMLPanel> as the parent of the CellBrowser (as seen in the GWT Showcase).
Do you have some sample code that will reproduce this?
below is the code for the composite...essentially, what I'd like to do is in another class, attached this composite to a flowpanel and do whatever with it...but, the reality is, I have to attach is directly to the RootPanel (or RootLayoutPanel)...any other abstraction causes it to bail
for example
FlowPanel fp = new FlowPanel();
V2_M76Rolodex v = new V2_M76Rolodex();
fp.add(v); // not going to work
RootPanel.get.add(v) works
public class V2_M76Rolodex extends Composite {
/*
a bunch of code here for getting data and
populating the tree - works, not at issue or relevant
*/
public V2_M76Rolodex() {
TreeViewModel model = new CustomTreeModel();
CellBrowser browser = new CellBrowser(model, null);
browser.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
browser.addStyleName("rolodex_cell_browser");
initWidget(browser);
}
}