I am trying to implement autocomplete with an NSTextField where the user will type in some string and suggestions will be fetched from an API to be displayed under the text field. Optionally display a progress indicator inside the text field. So far I have designed the UI in Xcode IB and hooked the event to get the text change event.
public class UserTextFieldDelegate: NSTextFieldDelegate
{
public NSTextField Username { get; set; }
public UserTextFieldDelegate()
{
}
public UserTextFieldDelegate(NSTextField username)
{
this.Username = username;
}
public override void Changed(NSNotification notification)
{
Console.WriteLine(Username.StringValue);
}
}
The API will return a list of objects which I need to bind with the datasource of autocomplete list.
How do I achieve this in Xamarin.Mac?
In the NSTextField.Changed, save the NSTextView from the NSNotification parameter and call your Rest API:
NSString NSFieldEditor = new NSString("NSFieldEditor");
NSTextView editor;
[Export("controlTextDidChange:")]
public void Changed(NSNotification notification)
{
editor = editor ?? notification.UserInfo.ObjectForKey(NSFieldEditor) as NSTextView;
SomeRestCall(nsTextField.StringValue);
}
Now with your Rest method, call the actual Rest api via a background queue and save/buffer the completion words returned in a string array and then call NSTextView.Complete on the NSTextView instance variable that you saved from the Changed method:
string[] completionWords = { };
void SomeRestCall(string search)
{
if (editor != null)
{
DispatchQueue.GetGlobalQueue(DispatchQueuePriority.Background).DispatchAsync(() =>
{
if (string.IsNullOrWhiteSpace(search))
completionWords = new string[] { };
else
// Fake a REST call...
completionWords = (new string[] { "sushi", "stack", "over", "flow" })
.Where((word) => word.StartsWith(search, StringComparison.CurrentCulture)).ToArray();
if (editor != null)
DispatchQueue.MainQueue.DispatchAsync(() => { editor?.Complete(null); });
});
}
}
In your implementation of INSTextFieldDelegate add the GetCompletions protocol and return completion words that you saved in the last step:
[Export("control:textView:completions:forPartialWordRange:indexOfSelectedItem:")]
public string[] GetCompletions(NSControl control, NSTextView textView, string[] words, NSRange charRange, ref nint index)
{
requestor = null;
return completionWords;
`}
Related
Goal
Provided a list of labels (and a moustache or other style string template), I want to dynamically create some input boxes, and then replace wherever the parameter exists in the template with a 2 way binding of what is in the input box. I am able to create the input boxes, but no idea how I can substitute the values for them into the template (or whether this is even possible in Blazor).
Example
public class TemplateService
{
private List<NameValuePairs> _labels = new List<NameValuePairs>
{
new NameValuePairs(){Name="Name", Value="John"},
new NameValuePairs(){Name="Age", Value="27"},
new NameValuePairs(){Name="Height", Value=""},
new NameValuePairs(){Name="Hair Colour", Value=""},
};
public Task<Template> GetTemplateByIdAsync(int id)
{
Template t = new Template() { Labels = _labels, Text = #"Hi, my name is {{Name}}. I am {{Age}} years old" };
return Task.FromResult(t);
}
}
#inject TemplateService TemplateService
<h3>Text Templates</h3>
#if (template == null)
{
<p><em>Loading...</em></p>
}
else
{
#foreach (var label in template.Labels)
{
<label>
#label.Name :
<input #bind-value="label.Value" #bind-value:event="oninput" />
</label>
}
<h4>Model</h4>
#foreach (var l in template.Labels)
{
<p>#l.Value</p>
}
<h4>Interpolated</h4>
//HERE I WANT TO DISPLAY A RESULT OF template.text WHEREBY {{Name}} WOULD BE SUBSTITUTED WITH JOHN IN THIS CASE, AND WOULD CHANGE AS THE INPUTBOX FOR NAME IS TYPED IN
}
#code {
private Template template;
protected override async Task OnInitializedAsync()
{
template = await TemplateService.GetTemplateByIdAsync(1);
}
}
It is the Interpolated bit in the view I am stuck with. I have control over the service so thought about replacing {{Age}} with a function like #FindValueForName("Age"), but not sure if this could be inserted as executable code instead of a literal string. Any thoughts of how this could be achieved in any way?
If you can re-structure a bit, you could try this
public class Template
{
public List<NameValuePairs> Labels { get; set; }
public Func<string> Text { get; set; }
}
public class TemplateService
{
private List<NameValuePairs> _labels = new List<NameValuePairs>
{
new NameValuePairs(){Name="Name", Value="John"},
new NameValuePairs(){Name="Age", Value="27"},
new NameValuePairs(){Name="Height", Value=""},
new NameValuePairs(){Name="Hair Colour", Value=""},
};
public string GetListValue(string Name) => _labels.Find(lbl=>lbl.Name==Name).Value;
public Task<Template> GetTemplateByIdAsync(int id)
{
Template t = new Template()
{
Labels = _labels,
Text = () => $#"Hi, my name is {GetListValue("Name")}. I am {GetListValue("Age")} years old" };
return Task.FromResult(t);
}
}
...
<h4>#template.text()</h4>
i am working with a .net application where i have a web service that returns values in array form and now this array values i want to pass to a class and also as a reference to a private object. But since i am fresh new in programming i do not know how where an with what logic to start.
This is the private obj i created and i want to pass those references where CT is the array type and clsIn is the info that comes from another class but i have no idea how to pass neither of them.
private object TotInfo(clsIn In, CT ct)
{
TotInfo objFromCD = new TotInfo();
return objFromCD;
}
And here is the new class i have created that where i want to pass all the values from clsIn and CT:
public class TotInfo
{
// Object properties
private string LAST_OFFER;
private string LAST_OFFER_DATE;
private string CLOSING_REASON;
private string _NO;
private string _STATUS;
#region "GET/SET Property"
public string NO
{
get { return _NO; }
set { _NO = value; }
}
public string LAST_OFFER
{
get { return _LAST_OFFER; }
set { _LAST_OFFER = value; }
}
public string LAST_OFFER_DATE
{
get { return _LAST_OFFER_DATE; }
set { _LAST_OFFER_DATE = value; }
}
public string CLOSING_REASON
{
get { return _CLOSING_REASON; }
set { _CLOSING_REASON = value; }
}
public string STATUS
{
get { return _STATUS; }
set { _STATUS = value; }
}
#endregion
#region "Costruttori"
public CardsTotInfo() { }
public CardsTotInfo(string No, string lastOffer, string lastOfferDate, string closingReason, string status)
{
this.NO = No;
this.LAST_OFFER = lastOffer.ToUpper();
this.LAST_OFFER_DATE = lastOfferDate.ToUpper();
this.CLOSING_REASON = closingReason.ToUpper();
this.STATUS = status.ToUpper();
}
}
I have passed, or better say i think i have passed in the correct way the values of clsIn but i do not know how to pass the properties of the array type CT[].
I really need help.
Thank you in advance.
If CT is an object array and the data you get from the web service always comes in the same order, for instance using an arbitrary example:
object[] CT = { 1, DateTime.Now, "foo", true }
If you know that each property data inside the array will always be at the same index (you will always have a int in index 0 representing an Id, and a DateTime on index 1 representing the last offer day and so on)
I would say you need to set each property "manually":
private object TotInfo(clsIn In, CT ct)
{
TotInfo objFromCD = new TotInfo();
//get data from DB
//set the data from the array into the class properties
objFromCD.Id = (int)ct[0];
objFromCD.LastOfferDate = (DateTime)ct[1];
objFromCD.ClosingReason = (string)ct[2];
objFromCD.Available = (bool)ct[3];
return objFromCD;
}
I have a class which looks like:
private ObservableCollection<string> _areaCollection;
private List<string> _removedAreas;
private List<string> _addedAreas;
public Constructor()
{
_areaCollection = new ObservableCollection<string>();
_areaCollection.CollectionChanged += AreaCollectionChanged;
_removedAreas = new List<string>();
_addedAreas = new List<string>();
}
public IEnumerable<string> Areas { get { return _areaCollection; } }
public IEnumerable<string> AddedAreas
{
get { return _addedAreas; }
}
public IEnumerable<string> RemovedAreas
{
get { return _removedAreas; }
}
public void DisableArea(string areaAlias)
{
if (_areaCollection.Contains(areaAlias))
_areaCollection.Remove(areaAlias);
}
public void EnableArea(string areaAlias)
{
if (!_areaCollection.Contains(areaAlias))
_areaCollection.Add(areaAlias);
}
private void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var item = e.NewItems.Cast<string>().First();
if (_addedAreas.Contains(item) == false)
{
_addedAreas.Add(item);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
var item = e.OldItems.Cast<string>().First();
if (_removedAreas.Contains(item) == false)
{
_removedAreas.Add(item);
}
}
}
Then in my controller on a Update actionresult I call a method to update the properties:
private bool UpdateProperties(IUser current, IUser update, out IUser result)
{
result = current;
// Update areas
...
return true;
}
So here I want the following result:
_areaCollection of the result contains the updated list
_removedAreas contains the areas which were removed based on the update
_addedAreas contains the areas which were added based on the update
But I am stuck on this.
I believe you are approaching your problem in the wrong way. From what I understand what you want is to know the differences between two collections. For this purpose you do not need ObservableCollection at all, you just need to filter which elements are removed and which are added.
Assuming current and updated both contains a read/write property called Areas:
var updatedSet = new HashSet<string>(updated.Areas);
var currentSet = new HashSet<string>(current.Areas);
var added = updatedSet.Where(a => !currentSet.Contains(a));
var removed = currentSet.Where(a => !updatedSet.Contains(a));
current.Areas = updated.Areas;
If you really need to keep your Areas property read-only you may change the last line with the following code:
foreach(var item in added)
current.EnableArea(item);
foreach(var item in removed)
current.DisableArea(item);
Which is really inefficient.
I want add search logic for my application (IOS8). I have simple MvxTableViewController and display my data by UITableViewSource. Here is:
...controller:
MvxViewFor(typeof(MainViewModel))]
partial class MainController : MvxTableViewController
{
public MainController(IntPtr handle) : base(handle) { }
public override void ViewDidLoad()
{
base.ViewDidLoad();
// make background trasnsparent page
this.View.BackgroundColor = UIColor.Clear;
this.TableView.BackgroundColor = UIColor.Clear;
this.NavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
this.SetBackground ();
(this.DataContext as MainViewModel).PropertyChanged += this.ViewModelPropertyChanged;
}
private void SetBackground()
{
// set blured bg image
}
private void ViewModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var viewModel = this.ViewModel as MainViewModel;
if (e.PropertyName == "Title")
{
this.Title = viewModel.Title;
}
else if (e.PropertyName == "Topics")
{
var tableSource = new TopicTableViewSource(viewModel.Topics);
tableSource.TappedCommand = viewModel.NavigateToChildrenPageCommand;
this.TableView.Source = tableSource;
this.TableView.ReloadData();
}
}
I read about search in IOS and choosed UISearchController for IOS8 app. But I don't understand, how I can add this controller to my view :(
I found sample from Xamarin (TableSearch) - but they don't use UITableViewSource and I don't understand what I should do with this.
I tried add controller:
this.searchController = new UISearchController (this.searchTableController)
{
WeakDelegate = this,
DimsBackgroundDuringPresentation = false,
WeakSearchResultsUpdater = this,
};
this.searchController.SearchBar.SizeToFit ();
this.TableView.TableHeaderView = searchController.SearchBar;
this.TableView.WeakDelegate = this;
this.searchController.SearchBar.WeakDelegate = this;
what should I do in this.searchTableController? Do I need move my display logic there?
Yes. The "searchTableController" should be responsible for the presentation of search results.
Here is the test project (native, not xmarin) which help you understand.
The searchController manages a "searchBar" and "searchResultPresenter". His not need add to a view-hierarchy of the carrier controller. When user starts typing a text in the "searchBar" the "SearchController" automatically shows your SearchResultPresenter for you.
Steps:
1) Instantiate search controller with the SearchResultsPresenterController.
2) When user inputs text in the search-bar you should invoke a your own service for the search. Below a sample of code..
#pragma mark - UISearchResultsUpdating
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
NSString *searchString = searchController.searchBar.text;
if (searchString.length > 1)
{
// TODO - call your service for the search by string
// this may be async or sync
// When a data was found - set it to presenter
[self.searchResultPresenter dataFound:<found data>];
}
}
3) In the search presenter need to reload a table in the method "dataFound:"
- (void)dataFound:(NSArray *)searchResults
{
_searchResults = searchResults;
[self.tableView reloadData];
}
Here are some advice on how to use the UISearchController with Xamarin.iOS.
Create a new class for the results table view subclassing UITableViewSource. This is gonna be the view responsible of displaying the results. You need to make the items list of that table view public.
public List<string> SearchedItems { get; set; }
In your main UIViewController, create your UISearchController and pass your result table view as an argument. I added some extra setup.
public UISearchController SearchController { get; set; }
public override void ViewDidLoad ()
{
SearchController = new UISearchController (resultsTableController) {
WeakDelegate = this,
DimsBackgroundDuringPresentation = false,
WeakSearchResultsUpdater = this,
};
SearchController.SearchBar.SizeToFit ();
SearchController.SearchBar.WeakDelegate = this;
SearchController.HidesNavigationBarDuringPresentation = false;
DefinesPresentationContext = true;
}
The best way to add the search bar to your UI in term of user experience, in my opinion, is to add it as a NavigationItem to a NavigationBarController.
NavigationItem.TitleView = SearchController.SearchBar;
Add methods to perform the search in the main UIViewController:
[Export ("updateSearchResultsForSearchController:")]
public virtual void UpdateSearchResultsForSearchController (UISearchController searchController)
{
var tableController = (UITableViewController)searchController.SearchResultsController;
var resultsSource = (ResultsTableSource)tableController.TableView.Source;
resultsSource.SearchedItems = PerformSearch (searchController.SearchBar.Text);
tableController.TableView.ReloadData ();
}
static List<string> PerformSearch (string searchString)
{
// Return a list of elements that correspond to the search or
// parse an existing list.
}
I really hope this will help you, good luck.
I'm trying to write some activities with C# instead of the designer and XAML. VS2010 has been buggy and very slow for that, and it also has very poor compilation support (for variables names, properties and so on).
So I'm trying to create activities by inheriting from the Activity class directly, but I'm encountering a snag.
Here's my code:
public class TestActivity : Activity
{
public InArgument<string> Username { get; set; }
public InArgument<string> Password { get; set; }
public OutArgument<bool> ValidCredential { get; set; }
public OutArgument<ProvisioningRole> Role { get; set; }
public OutArgument<Guid> Guid { get; set; }
protected override Func<Activity> Implementation
{
get
{
return () =>
{
return new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username,
Password = this.Password,
Guid = this.Guid,
Result = this.ValidCredential
},
new If()
{
Condition = this.ValidCredential,
Then = new GetUserRoleActivity()
{
Username = this.Username,
Password = this.Password,
Result = this.Role
}
},
}
};
};
}
set { base.Implementation = value; }
}
}
The problem is with the If(), the condition. It's supposed to be an InArgument, but this.ValidCredential is an OutArgument. I've tried creating a Variable, assign the value of ValidCredential to it. I also tried to put the result of AuthenticateUserActivity in the variable and then assign it to ValidCredential, but I get an error saying the To property of Assign needs to be specified.
I've looked around for proper tutorials, but all I found was an MSDN article that had a quick and dirty code implementation, and it used literals instead of the passed arguments, so no help from there.
I found out how to do it. You just need to create new InArgument from the original one. There is a constructor that takes an expression for it.
Username = new InArgument<bool>((ActivityContext c) => this.ValidCredential.Get(c))
So I changed my whole activity to
return new CompensableActivity()
{
Body = new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Guid = this.Guid.Out(),
Result = this.ValidCredential.Out()
},
new If(this.ValidCredential.In())
{
Then = new GetUserRoleActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Result = this.Role.Out()
},
Else = new Assign<ProvisioningRole>()
{
To = this.Role.Out(),
Value = ProvisioningRole.User
}
}
}
},
};
In and Out being extension methods I wrote:
public static class WorkflowExtensions
{
#region In
public static InArgument<T> In<T>(this InArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
public static InArgument<T> In<T>(this OutArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
#endregion
#region Out
public static OutArgument<T> Out<T>(this InArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
public static OutArgument<T> Out<T>(this OutArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
#endregion
}
And now all is well!
You should be able to get this to work. The basic approach should be to use a Variable to store data, use an OutArgument to get data out of activities into the Variable and InArguments to get data from a Variable into an activity.
Also note that the expressions to tie InArguments to Variables are VisualBasicValue expressions. So something like:
Condition = new VisualBasicValue("System.DateTime.Now.Hour < 12")
This blog post isn't about using arguments and variables but shows a couple of examples.
Going to shamelessly plug my own library that I ended up making for this:
http://code.google.com/p/system-transactions/
Allows basic compensation of code without the ginormous hassle of WF. Also, compiles properly and is easily debuggable.