I have this razor code and I intend to disable the entire component (not responding to user inputs and visibly distinctive) based on a variable that changes on some event:
<div #bind-Enabled=#NoItemSelected >
<Component01 Param1="#DataItem01" />
</div>
#code{
public DataItem DataItem01 {get; set;}
public bool NoItemSelected => DataItem01 != null;
}
Obviously trying to set the div's enabled state is not working.
The component contains many elements so I can't disable them one-by-one based on an input parameter.
Any idea how I should set the disabled state to the entire component?
You can place if statements around your components
#if (NoItemSelected == false)
{
<Component01 Param1="#DataItem01" />
}
Related
Is it possible to set the binding for one of my properties to be OneTime when the layout is used in a MvxRecyclerView? I set it to OneTime, but it keeps rebinding as I scroll through the list. This is the TextView that it keeps rebinding to:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/text_huge"
android:textColor="#color/white"
local:MvxBind="Text IsAddedToCart, Converter=BoolToFontAwesome, ConverterParameter=fa-shopping-cart|fa-cart-plus; Style ., Converter=String, ConverterParameter=fonts/fontawesome.ttf, Mode=OneTime" />
I set the binding mode to OneTime for the Style, but it keeps rebinding as I scroll through the items in the list (I know because I put a break point). This is the class for my Style binding:
public class StyleTextViewBinding : MvxAndroidTargetBinding
{
readonly TextView _textView;
public StyleTextViewBinding(TextView textView) : base(textView)
{
_textView = textView;
}
#region implemented abstract members of MvxConvertingTargetBinding
protected override void SetValueImpl(object target, object value)
{
var font = Typeface.CreateFromAsset(_textView.Context.Assets, value.ToString());
_textView.Typeface = font;
// I put a break point here, and I can see that it keeps rebinding
}
#endregion
public override Type TargetType
{
get { return typeof(string); }
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneTime; }
}
}
OneTime bindings will only work as such if you do not recycle your Views. Meaning if you don't scroll and change what is bound to the Style property from the ViewModel. Then it will only fire once.
Since RecyclerView uses the ViewHolder pattern, the views themselves are not recreated every time. This means, in order to have the correct state in the shown items, we need to re-bind them every time they reappear on the screen.
I have two datatemplates. One is the default and the other one is for when the user selects this item. I need to give the selected item double width and height of the normal template. How can I do this?
What you want to do is not difficult, but it is not solved by swapping the data template. Instead, it is accomplished by using Visual States in XAML. A Visual State allows you to create multiple "views" of your XAML (for example, what it looks like when it is selected and when it is not selected) and to switch between those easily. Swapping data templates is a big deal, Mostafa, and can result in your UI flickering because the underlying subsystem has to re-render so many parts of the visual tree.
If you want to learn more about the Visual States, you might read over the blog article I wrote on the same subject.
http://blog.jerrynixon.com/2013/11/windows-81-how-to-use-visual-states-in.html
The only problem now is to figure out how to trigger the visual state when the item in a gridview or listview is selected. First, you should know that IsSelected is a property on the gridviewitem or listviewitem control that houses your item. However, it's tricky to reach that property and the most common approach is to sub-class your gridview/listview and override PrepareContainerForItemOverride and set the binding in code-behind.
Like this:
class MyModel
{
public bool IsSelected { get; set; }
}
class MyList : Windows.UI.Xaml.Controls.ListView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
var model = item as MyModel;
var listViewItem = element as Windows.UI.Xaml.Controls.ListViewItem;
var binding = new Windows.UI.Xaml.Data.Binding
{
Source = model,
Mode = Windows.UI.Xaml.Data.BindingMode.TwoWay,
Path = new PropertyPath(nameof(model.IsSelected)),
};
listViewItem.SetBinding(Windows.UI.Xaml.Controls.ListViewItem.IsSelectedProperty, binding);
base.PrepareContainerForItemOverride(element, item);
}
}
I hope this helps.
Best of luck!
In a MvvmCross app, I have a page with the classic chat behavior (WhatsApp like): this page shows the history of messages exchanged between two users with the last message at the bottom of the list.
I've successfully implemented the view in Windows Phone 8.1, but I'm struggling with a problem in Android.
I'll give you a short introduction and description of my problem and next I'll go through technical details.
INTRODUCTION
Actually, my need is to apply different style to messages sent by different users: tipically align left messages sent from other user and align right messages sent by me (I do this through the weight property); I need to apply a different drawable background and set different gravity property also.
I use custom binding because, AFAIK, those properties cannot be binded with classic binding: local:MvxBind="Gravity MyPropery" doesn't work because there is no Gravity property.
So, I have of course two axml files:
the first one contains the Mvx.MvxListView
the second one contains the item template for MvxListView
And I've created three different custombinding (for Background, Gravity and Weight) following these guides:
http://slodge.blogspot.it/2013/06/n28-custom-bindings-n1-days-of-mvvmcross.html
In MvvmCross how do I do custom bind properties
THE PROBLEM
I want that, when a user opens the chat View, the list widget shows automatically the last message. To accomplish this, I scroll programmatically the list to the last message and this seems to be the problem.
If I don't scroll programmatically, when I open the page and scroll manually to the end of the page, all custom bindings are applied successfully: I can see messages aligned right and left, with correct background and weight applied.
If I force the scroll programmatically, when I open the page I see a strange behavior: all the messages are present (classic binding, such as Text property, have been successfully applied), but custom bindings are missing. All the messages have the same background and are all left aligned.
BUT, if I scroll manually up and down, the custom binding are processed and the messages are displayed with right style.
DEBUG ANALYSIS
To debug the behaviour I've put a simple static counter in a custom binding procedure to track every time the function is processed.
public class LinearLayoutWeightTargetBinding : MvxAndroidTargetBinding
{
public static int debugCounter = 0;
public LinearLayoutWeightTargetBinding(object target) : base(target)
{
}
protected LinearLayout MyTarget
{
get { return (LinearLayout)Target; }
}
public override Type TargetType { get { return typeof(bool); } }
protected override void SetValueImpl(object target, object value)
{
var ll = (LinearLayout)target;
var itsMe = (bool)value;
var weight = itsMe ? (float)20.0 : (float)5.0;
var layoutParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WrapContent, weight);
ll.LayoutParameters = layoutParams;
Log.Debug("MeeCHAT", string.Format("LinearLayoutWeightTargetBinding::SetValueImpl::ItsMe:{0} - counter:{1}", itsMe, ++debugCounter));
}
public override MvxBindingMode DefaultMode { get {return MvxBindingMode.TwoWay;} }
}
By this way I saw that actually by scrolling up and down the custom bindings are applied (debugCounter increases correctly).
BUT when I apply the programmatically scroll, only the first 10 items are processed by the custom bindings and this seems the reason why I see the messages without the right style. Because I have a long list, only the first 10 items are processed but they are not visible (they are out of the visible area) and the visibile items have not been processed.
TECHNICAL DETAILS
Here are some details related to technical aspects of my app. I try to give you all important aspects.
ORGANIZATION OF THE VIEWS
By following the approach described by Greg Shackles in this article http://gregshackles.com/presenters-in-mvvmcross-navigating-android-with-fragments/ I have just one general Activity for the app and one Fragment for each View; then through a Presenter is possible to activate the right ViewModel and manage the stack of the navigation.
The Fragment for the View where I have the Mvx.MvxListView widget is
public class MyMatchersChatView : MvxFragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignore = base.OnCreateView(inflater, container, savedInstanceState);
var result = this.BindingInflate(Resource.Layout.MyMatchersChatView, null);
var headerFrame = result.FindViewById<FrameLayout>(Resource.Id.headerFrameMyMatchersChatView);
var headerWidget = new HeaderWidget() { ViewModel = this.ViewModel };
var tran = ChildFragmentManager.BeginTransaction();
tran.Add(headerFrame.Id, headerWidget, "headerMyMatchersChat");
tran.Commit();
var listView = result.FindViewById<MvxListView>(Resource.Id.messagesList);
listView.SetSelection(listView.Adapter.Count - 1); // Scroll to the end of the list
return result;
}
}
The statement listView.SetSelection(listView.Adapter.Count - 1); force the list to scroll to the end.
Last two things: how the custom bindings are registered and how are applied in axml file.
REGISTRATION OF CUSTOM BINDING
In Setup.cs I have:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<LinearLayout>("CustomWeight",
(b) => new LinearLayoutWeightTargetBinding(b)));
}
APPLYING OF CUSTOM BINDING
In my axml I have:
<LinearLayout
android:orientation="horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
local:MvxBind="CustomWeight IsCurrentUser">
LISTVIEW AND VIEWMODEL
Here is the code of ListView
<Mvx.MvxListView
android:id="#+id/messagesList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="ItemsSource MyMessages"
local:MvxItemTemplate="#layout/mymatcherschatview_itemtemplate" />
and the property in ViewModel
private IEnumerable<MyMatchMessageModel> _myMessages;
public IEnumerable<MyMatchMessageModel> MyMessages
{
get { return _myMessages; }
set
{
_myMessages = value;
RaisePropertyChanged(() => MyMessages);
}
}
ENVIRONMENT
Finally, here is my environment:
Visual Studio 2015
MvvmCross 3.5.1
Core targets: .NET Framework 4.5, Windows 8, ASP.NET Core 5.0, Windows Phone 8.1, Xamarin.Android, Xamarin.iOS, Xamarin.iOS (Classic)
The Android app target is API Level 19 (Xamarin.Android v4.4 Support)
Xamarin 3.11.1450.0
Xamarin.Android 5.1.6.7
Someone can help me to understand if I'm doing something wrong?
Thanks for reading and for any help!
>>EDIT 1<<
I've changed my layout by adding stackFromBottom and transcriptMode properties and by removing the scrolling to below programmatically in Fragment obtaining an auto-scroll behavior, but the problem still remains: to see messages with correct style I have to manually scroll up and down (to activate the custom bindings)
Here is the new axml...
<Mvx.MvxListView
android:id="#+id/messagesList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
local:MvxBind="ItemsSource MyMessages"
local:MvxItemTemplate="#layout/mymatcherschatview_itemtemplate" />
...and the new code in Fragment
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignore = base.OnCreateView(inflater, container, savedInstanceState);
var result = this.BindingInflate(Resource.Layout.MyMatchersChatView, null);
var headerFrame = result.FindViewById<FrameLayout>(Resource.Id.headerFrameMyMatchersChatView);
var headerWidget = new HeaderWidget() { ViewModel = this.ViewModel };
var tran = ChildFragmentManager.BeginTransaction();
tran.Add(headerFrame.Id, headerWidget, "headerMyMatchersChat");
tran.Commit();
return result;
}
First thing I would do is to make sure that your custom binding is always getting called.
Set a breakpoint on the SetValueImpl() method and check it´s getting called on those problematic items. If that happens, then the issue relies on the view no getting updated for any reason and you should work on that. If it doesn´t break, you will know for sure it´s a custom binding problem (possibly a bug) in MvxAdapter.
If you find out it´s the second one. I would suggest getting rid of your custom binding and creating your own ChatListAdapter : MvxAdapter as follows:
public class CoolChatListAdapter : MvxAdapter
{
public CoolChatListAdapter(Context context, IMvxAndroidBindingContext bindingContext) : base(context, bindingContext)
{
}
protected override View GetBindableView(View convertView, object source, int templateId)
{
var item = source as MyMatchMessageModel;
var weight = item.IsCurrentUser ? (float) 20.0 : (float) 5.0;
var ll = (LinearLayout) convertView;
var layoutParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WrapContent, weight);
ll.LayoutParameters = layoutParams;
return base.GetBindableView(convertView, source, templateId);
}
}
Then, in your android view:
var adapter = new ChatListAdapter(this, (IMvxAndroidBindingContext)BindingContext);
_chatList = FindViewById<MvxListView>(Resource.Id.chat_list_view);
_chatList.Adapter = adapter;
The value is still preserved in asp.net webform page even if I disable the view state and control state. Control's value is still preserved in postback even after I disable Enableviewstate as false for that page and disable the control state by overriding SavePageStateToPersistenceMedium() as empty.
/*value is still preserving in asp.net webform page
even if disable view state and control state*/
public class CustomTextBox : TextBox
{
public CustomTextBox()
{
}
//this method is ipostbackdatahandler's one
protected override void LoadControlState(object savedState)
{
//doing ntng here means we are not saving anything by this way
}
//this method is ipostbackdatahandler's one
protected override void RaisePostDataChangedEvent()
{
}
protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
{
//writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, "Green");
writer.AddStyleAttribute(HtmlTextWriterStyle.Padding, "5px");
base.AddAttributesToRender(writer);
}
}
How can I resolve this situation?
The value of the textbox is held in the form post data so ASP.NET is going to automatically preserve it.
If you want the textbox to come back empty, add code such as this, perhaps during prerender.
this.MyTextbox.Text = "";
I have an application with a popup menu. I'd like to use the popup in the usual way (i.e., it should appear when the user right-clicks anywhere in the window), but I'd also like to attach it to the main MenuBar at the top of the window. I'm not sure how to do this.
I'd thought it would as simple as calling
myJMenuBar.add(myPopupMenu)
but this doesn't work.
JMenuBar.add() wants a JMenu parameter, not a JPopupMenu.
Does anyone have any suggestions?
Instead of trying to reuse the JPopupMenu object, the best approach would be to encapsulate the actions that the menus perform, and reuse those. The popup would trigger those actions, as would the menu items.
From the Action JavaDoc:
In addition to the actionPerformed method defined by the ActionListener interface, this interface allows the application to define, in a single place:
One or more text strings that describe the function. These strings can be used, for example, to display the flyover text for a button or to set the text in a menu item.
One or more icons that depict the function. These icons can be used for the images in a menu control, or for composite entries in a more sophisticated user interface.
The enabled/disabled state of the functionality. Instead of having to separately disable the menu item and the toolbar button, the application can disable the function that implements this interface. All components which are registered as listeners for the state change then know to disable event generation for that item and to modify the display accordingly.
and
JPopupMenu, JToolBar and JMenu all provide convenience methods for creating a component and setting the Action on the corresponding component. Refer to each of these classes for more information.
I had the same issue. A right-mouse-click as well as a top menu with exactly the same (complicated) set of menu items. The 'Action' class is something to consider if you are talking about enablement choices, but it's not dealing with visibility and in my case there was also a dynamic list of entries based on a current selection that I wanted to reuse.
So I ended up implementing a 'Bridge' design pattern (I think) for the methods I actually use (add() and addSeparator()):
public static class MenuBridge
{
private JPopupMenu popupMenu;
private JMenu menu;
public MenuBridge(JPopupMenu popupMenu)
{
this.popupMenu = popupMenu;
}
public MenuBridge(JMenu menu)
{
this.menu = menu;
}
public void addSeparator()
{
if(popupMenu!=null) popupMenu.addSeparator();
else menu.addSeparator();
}
public void add(JMenuItem item)
{
if(popupMenu!=null) popupMenu.add(item);
else menu.add(item);
}
}
So then I can write a reusable method that computes the menu items and synchronize my right mouse click with the top-level menu:
public void addTaskMenuItems(DefaultMenu menu, List<MDProcTask> taskList)
{
...
menu.add()/menu.addSeparator()
...
}
addTaskMenuItems(new DefaultMenu(popupMenu),taskList);
...
taskMenu.addMenuListener( new MenuListener() {
public void menuCanceled(MenuEvent menuevent)
{
}
public void menuDeselected(MenuEvent menuevent)
{
}
public void menuSelected(MenuEvent menuevent)
{
taskMenu.removeAll();
addTaskMenuItems( new DefaultMenu(taskMenu),getSelectedTasks());
taskMenu.revalidate();
}});