I am new to C# & Xamarin. I feel there would be some quick solution to achieve this, but I already consumed a day to find one. Please give me a hint so I can proceed in the right direction.
The method is to Resize a Window after getting Height & Width. The issue is the GetWidth() and GetHeight() runs on async mode while the main thread sets the Window.SetFrame(...), hence I am not able to resize the window.
One solution I think is of cascading and setting the Window frame in the innermost block.
private void ResizeWindow()
{
_webView.EvaluateJavaScript("document.readyState",
(complete, error) =>
{
if (complete != null)
{
GetHeight();
GetWidth();
CGRect windowRect = Window.Frame;
windowRect.Size = new CGSize(_width, _height);
Window.SetFrame(windowRect,true);
}
});
}
private void GetWidth()
{
_webView.EvaluateJavaScript("document.body.scrollWidth",
(scrollWidth, wError) =>
{
if (scrollWidth != null && nfloat.TryParse(scrollWidth.ToString(), out nfloat value))
{
_width = value;
}
});
}
private void GetHeight()
{
_webView.EvaluateJavaScript("document.body.scrollHeight",
(scrollHeight, hError) =>
{
if (scrollHeight != null && nfloat.TryParse(scrollHeight.ToString(), out nfloat value))
{
_height = value;
}
});
}
Xamarin.iOS already has a TaskCompletionSource-based wrapper for WKWebView.EvaluateJavaScript.
So you can just await the call to EvaluateJavaScriptAsync for the NSValue-based result and then convert it to a C# value type.
Since I know that javascript being run returns a number, I can cast the NSObject return (that actually is an NSValue) to a NSNumber and obtain a C# long from it.
Example:
var result = await webview.EvaluateJavaScriptAsync("document.body.scrollWidth");
var width = (result as NSNumber).Int64Value;
result = await webview.EvaluateJavaScriptAsync("document.body.scrollHeight");
var height = (result as NSNumber).Int64Value;
Related
I would like to make some code for a game a little more compact. Here is the original.
if (killPosX != 0) // float x position you would like the object to be destroyed at
{
killX = true; // automatically makes the value of killX true
}
if (killPosY != 0) // float y position you would like the object to be destroyed at
{
killY = true; // automatically makes the value of killY true
}
if (killPosT != 0) // float position in time you would like the object to be destroyed at
{
killT = true; // automatically makes the value of killT true
}
And I want to turn it into something like this:
if ([killPosX, killPosY, killPosT] != 0)
{
[killPosX, killPosY, killPosT] = true;
}
How would I do that?
if (killPosX != 0)
{
killX = true;
}
Could be translated as follow
killX = (killPosX != 0) ? true : killX;
Or more simply
killX |= killPosX != 0;
if (killPosX != 0)
{
killX = true;
}
else
{
killX = false;
}
Could be translated as follow
killX = (killPosX != 0) ? true : false;
Or more simply
killX = killPosX != 0;
But since comparing floats using == or != is not advised, I would go for:
killX |= !Mathf.Approximately( killPosX, 0 );
killY |= !Mathf.Approximately( killPosY, 0 );
killT |= !Mathf.Approximately( killPosT, 0 );
AFAIK, there is no built-in syntax similar to what you wrote in order to achieve what you want.
I'm not sure if this applies to what you're working on (and there may be a better way in Unity), but it seems to me that one way to do it would be to make the KillN properties calculated on the value of their corresponding KillPosN property:
public class SomeClass
{
public float KillPosX { get; set; }
public float KillPosY { get; set; }
public float KillPosT { get; set; }
// Values are based on the related property values and cannot be set directly
public bool KillX => !IsRoughlyZero(KillPosX);
public bool KillY => !IsRoughlyZero(KillPosY);
public bool KillT => !IsRoughlyZero(KillPosT);
private static bool IsRoughlyZero(float input)
{
// Use a tolerance for comparing floating point numbers to 0
return Math.Abs(input) < .0000001;
}
}
I can't think of a way to do it as you suggest but it might be a little neater and more compact to use a variation of the approach hellium suggested:
public void fnc(ref bool t1, float t2) { t1 |= !Mathf.Approximately( t2, 0 ); }
fnc(ref KillX, KillPosX);
fnc(ref KillY, KillPosY);
fnc(ref KillT, KillPosT);
You could of course wrap it in a method which uses arrays as parameters but unless you need a generic way to do this for variable sized arrays, the setup cost is bulkier than just using discrete calls.
I've got a panel for text formatting and I've got to get sub- and superscript work but it can't be done by inserting \sub or \super into RTF (RTF is what I need in the end).
This is how I would set superscript by inserting \sub:
public void SetSuperscript()
{
ITextSelection selectedText = _textbox.Document.Selection;
if (selectedText != null)
{
ITextCharacterFormat charFormatting = selectedText.CharacterFormat;
charFormatting.Superscript= FormatEffect.On;
selectedText.CharacterFormat = charFormatting;
}
}
Now I know I can do it like that as well:
public void SetSuperscript()
{
ITextSelection selectedText = _textbox.Document.Selection;
if (selectedText != null)
{
ITextCharacterFormat charFormatting = selectedText.CharacterFormat;
charFormatting.Size /= 2;
charFormatting.Position = charFormatting.Size;
selectedText.CharacterFormat = charFormatting;
}
}
and it works just as fine. The problem is when it comes to creating a subscript:
public void SetSubscript()
{
ITextSelection selectedText = _textbox.Document.Selection;
if (selectedText != null)
{
ITextCharacterFormat charFormatting = selectedText.CharacterFormat;
charFormatting.Size /= 2;
charFormatting.Position = -charFormatting.Size;
selectedText.CharacterFormat = charFormatting;
}
}
This code above throws an exception "Value does not fall within the expected range." and it's caused by this line:
charFormatting.Position = -charFormatting.Size;
I'm assigning there a negative value (which is allright according to the documentation) because I need the selected text to be lower than normal text by half of its height. What am I doing wrong?
Daniel
I'm currently using the following code as a LineTransformer with an AvalonEdit TextEditor. I want to be able to highlight the current single search result with a selection, however the selection is barely visible because the formatting of the DocumentColorizingTransformer has precedence over showing highlighted text. How do I get the highlighted selection to show instead of or before the formatting?
public class ColorizeSearchResults : DocumentColorizingTransformer {
public ColorizeSearchResults() : base() {
SearchTerm = "";
MatchCase = false;
}
public string SearchTerm { get; set; }
public bool MatchCase { get; set; }
protected override void ColorizeLine(DocumentLine line) {
if (SearchTerm.Length == 0)
return;
int lineStartOffset = line.Offset;
string text = CurrentContext.Document.GetText(line);
int count = 0;
int start = 0;
int index;
while ((index = text.IndexOf(SearchTerm, start, MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) >= 0) {
base.ChangeLinePart(
lineStartOffset + index,
lineStartOffset + index + SearchTerm.Length,
(VisualLineElement element) => {
element.TextRunProperties.SetForegroundBrush(Brushes.White);
element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);
});
start = index + 1;
count++;
}
}
}
Example of formatting showing over selection
See http://avalonedit.net/documentation/html/c06e9832-9ef0-4d65-ac2e-11f7ce9c7774.htm for AvalonEdit render flow.
The selection layer is rendered before text. So if the text has background, it overrides the selection background. Fortunately we can set the background to Brush.Transparent (or a mix of the Selection.Brush and your own color).
Solution: I've modified the SelectionColorizer code to reset selection background to transparent:
class SelectionColorizerWithBackground : ColorizingTransformer
{
ICSharpCode.AvalonEdit.Editing.TextArea _textArea;
public SelectionColorizerWithBackground(
ICSharpCode.AvalonEdit.Editing.TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this._textArea = textArea;
}
protected override void Colorize(ITextRunConstructionContext context)
{
int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
int lineEndOffset = context.VisualLine.LastDocumentLine.Offset +
context.VisualLine.LastDocumentLine.TotalLength;
foreach (var segment in _textArea.Selection.Segments)
{
int segmentStart = segment.StartOffset;
if (segmentStart >= lineEndOffset)
continue;
int segmentEnd = segment.EndOffset;
if (segmentEnd <= lineStartOffset)
continue;
int startColumn;
if (segmentStart < lineStartOffset)
startColumn = 0;
else
startColumn = context.VisualLine.ValidateVisualColumn(
segment.StartOffset, segment.StartVisualColumn,
_textArea.Selection.EnableVirtualSpace);
int endColumn;
if (segmentEnd > lineEndOffset)
endColumn =
_textArea.Selection.EnableVirtualSpace
? int.MaxValue
: context.VisualLine
.VisualLengthWithEndOfLineMarker;
else
endColumn = context.VisualLine.ValidateVisualColumn(
segment.EndOffset, segment.EndVisualColumn,
_textArea.Selection.EnableVirtualSpace);
ChangeVisualElements(
startColumn, endColumn,
element => {
element.TextRunProperties.SetBackgroundBrush(
System.Windows.Media.Brushes.Transparent);
if (_textArea.SelectionForeground != null)
{
element.TextRunProperties.SetForegroundBrush(
_textArea.SelectionForeground);
}
});
}
}
}
To use the code you are supposed to do the following:
var lineTransformers = textEditor.TextArea.TextView.LineTransformers;
// Remove the original SelectionColorizer.
// Note: if you have syntax highlighting you need to do something else
// to avoid clearing other colorizers. If too complicated you can skip
// this step but to suffer a 2x performance penalty.
lineTransformers.Clear();
lineTransformers.Add(new ColorizeSearchResults());
lineTransformers.Add(
new SelectionColorizerWithBackground(textEditor.TextArea));
After I've tried my solutions extensively, I'd like to add a few points:
While my other solution above appears to work, you'll have some subpixel artefacts when the rectangles are supposed to be tiled. If that is unacceptable you can implement an IBackgroundRenderer. (That happens to be my chosen solution.) If you want some code you may request here, but I doubt whether it will be useful.
BTW, since your question is about search result, most likely you can use https://github.com/icsharpcode/AvalonEdit/blob/697ff0d38c95c9e5a536fbc05ae2307ec9ef2a63/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs unmodified (or modify it if you don't want the rounded borders).
You may use element.BackgroundBrush = Brushes.Magenta; instead of element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);. AvalonEdit appears to draw the background with a rectangle with 3px radius.
There is also a RichTextColorizer starting from AvalonEdit 5.01. I don't know how to use it though because it is not referenced in other files. And the (most likely unwanted) rounded rectangles in the previous paragraph are likely to be present.
So here's my final product based almost entirely off of the existing AvalonEdit SearchResultBackgroundRenderer.
This works a little different than my post's colorizer as you have to modify the search results manually instead of it doing it for you. But that may also save some computation time.
If your search doesn't use Regex, then you can easily modify SearchResult to instead just pass in a start offset and length for the constructor.
/// <summary>A search result storing a match and text segment.</summary>
public class SearchResult : TextSegment {
/// <summary>The regex match for the search result.</summary>
public Match Match { get; }
/// <summary>Constructs the search result from the match.</summary>
public SearchResult(Match match) {
this.StartOffset = match.Index;
this.Length = match.Length;
this.Match = match;
}
}
/// <summary>Colorizes search results behind the selection.</summary>
public class ColorizeSearchResultsBackgroundRenderer : IBackgroundRenderer {
/// <summary>The search results to be modified.</summary>
TextSegmentCollection<SearchResult> currentResults = new TextSegmentCollection<SearchResult>();
/// <summary>Constructs the search result colorizer.</summary>
public ColorizeSearchResultsBackgroundRenderer() {
Background = new SolidColorBrush(Color.FromRgb(246, 185, 77));
Background.Freeze();
}
/// <summary>Gets the layer on which this background renderer should draw.</summary>
public KnownLayer Layer {
get {
// draw behind selection
return KnownLayer.Selection;
}
}
/// <summary>Causes the background renderer to draw.</summary>
public void Draw(TextView textView, DrawingContext drawingContext) {
if (textView == null)
throw new ArgumentNullException("textView");
if (drawingContext == null)
throw new ArgumentNullException("drawingContext");
if (currentResults == null || !textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
foreach (SearchResult result in currentResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.BorderThickness = 0;
geoBuilder.CornerRadius = 0;
geoBuilder.AddSegment(textView, result);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
drawingContext.DrawGeometry(Background, null, geometry);
}
}
}
/// <summary>Gets the search results for modification.</summary>
public TextSegmentCollection<SearchResult> CurrentResults {
get { return currentResults; }
}
/// <summary>Gets or sets the background brush for the search results.</summary>
public Brush Background { get; set; }
}
In order to make use of the background renderer:
var searchColorizor = new ColorizeSearchResultsBackgroundRenderer();
textEditor.TextArea.TextView.BackgroundRenderers.Add(searchColorizor);
I'm working in a WPF mvvm environment.
I have some binded vars and data from cs file to xaml.
One is different from others: it is the index of the selected tab in my tabsCollection. When the user has more than one tab opened and has got mods to save, I show him a dialog. If he cliks "ok", he proceed with the change of the tab, if he clicks "cancel", the tab must remain the same.
this is my code:
private int p_SelectedDocumentIndex;
public int SelectedDocumentIndex{ get { return p_SelectedDocumentIndex; }
set {
if (tabsCollection.Count() > 1 && CanSave() == true)
{
if (dm.ShowMessage1(ServiceContainer.GetService<DevExpress.Mvvm.IDialogService>("confirmYesNo")))
{
p_SelectedDocumentIndex = value;
base.RaisePropertiesChanged("SelectedDocumentIndex");
}
//else {
// CODE FOR NOT CHANGE THE VALUE
//}
}
else {
p_SelectedDocumentIndex = value;
base.RaisePropertiesChanged("SelectedDocumentIndex");
}
}
}
So, the question is: how can I not apply the change in the "set" section? (like an undo, I think)
This is simpliest way to do it, but, if this approach is incorrect, how can I do?
Previous failed attempts:
1)
p_SelectedDocumentIndex = p_SelectedDocumentIndex
base.RaisePropertiesChanged("SelectedDocumentIndex");
2)
base.RaisePropertiesChanged("SelectedDocumentIndex");
3)
nothing in the else branch
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => SelectedDocumentIndex= p_SelectedDocumentIndex ), DispatcherPriority.Send);
This call arranges to revert the UI state to where it was before the operation started
I solved it. I've took the solution from here:
http://blog.alner.net/archive/2010/04/25/cancelling-selection-change-in-a-bound-wpf-combo-box.aspx
public int SelectedDocumentIndex{ get { return p_SelectedDocumentIndex; }
set {
// Store the current value so that we can
// change it back if needed.
var origValue = p_SelectedDocumentIndex;
// If the value hasn't changed, don't do anything.
if (value == p_SelectedDocumentIndex)
return;
// Note that we actually change the value for now.
// This is necessary because WPF seems to query the
// value after the change. The combo box
// likes to know that the value did change.
p_SelectedDocumentIndex = value;
if (tabsCollection.Count() > 1 && CanSave() == true)
{
if (!dm.ShowMessage1(ServiceContainer.GetService<DevExpress.Mvvm.IDialogService>("confirmYesNo")))
{
Debug.WriteLine("Selection Cancelled.");
// change the value back, but do so after the
// UI has finished it's current context operation.
Application.Current.Dispatcher.BeginInvoke(
new Action(() =>
{
Debug.WriteLine("Dispatcher BeginInvoke " + "Setting CurrentPersonCancellable.");
// Do this against the underlying value so
// that we don't invoke the cancellation question again.
p_SelectedDocumentIndex = origValue;
DocumentPanel p = tabsCollection.ElementAt(p_SelectedDocumentIndex);
p.IsActive = true;
base.RaisePropertiesChanged("SelectedDocumentIndex");
}),
System.Windows.Threading.DispatcherPriority.ContextIdle,
null
);
// Exit early.
return;
}
}
// Normal path. Selection applied.
// Raise PropertyChanged on the field.
Debug.WriteLine("Selection applied.");
base.RaisePropertiesChanged("SelectedDocumentIndex");
}
}
If your VM class is derived from DependencyObject then you can change your property to a DependecyProperty with a coerce callback which enables "undo" as follows:
public int SelectedDocumentIndex
{
get { return (int)GetValue(SelectedDocumentIndexProperty); }
set { SetValue(SelectedDocumentIndexProperty, value); }
}
public static readonly DependencyProperty SelectedDocumentIndexProperty =
DependencyProperty.Register("SelectedDocumentIndex", typeof(int), typeof(MyViewModel), new PropertyMetadata(0,
(d, e) =>
{
//Callback after value is changed
var vm = (MyViewModel)d;
var val = (int)e.NewValue;
}, (d, v) =>
{
//Coerce before value is changed
var vm = (MyViewModel)d;
var val = (int)v;
if (vm.tabsCollection.Count() > 1 && vm.CanSave() == true)
{
if (vm.dm.ShowMessage1(ServiceContainer.GetService<DevExpress.Mvvm.IDialogService>("confirmYesNo")))
{
//no coerce is needed
return v;
}
else
{
//should coerce to the previous value
return VM.SelectedDocumentIndex;
}
}
else
{
//no coerce is needed
return v;
}
}));
Here is the situation, I am developing a binary search tree and in each node of the tree I intend to store the height of its own for further balancing the tree during avl tree formation. Previously I had an iterative approach to calculate the height of a node during balancing the tree like the following.
(The following code belongs to a class called AVLTree<T> which is a child class of BinarySearchTree<T>)
protected virtual int GetBalance(BinaryTreeNode<T> node)
{
if(node != null)
{
IEnumerable<BinaryTreeNode<T>> leftSubtree = null, righSubtree = null;
if (node.Left != null)
leftSubtree = node.Left.ToEnumerable(BinaryTreeTraversalType.InOrder);
if (node.Right != null)
righSubtree = node.Right.ToEnumerable(BinaryTreeTraversalType.InOrder);
var leftHeight = leftSubtree.IsNullOrEmpty() ? 0 : leftSubtree.Max(x => x.Depth) - node.Depth;
var righHeight = righSubtree.IsNullOrEmpty() ? 0 : righSubtree.Max(x => x.Depth) - node.Depth;
return righHeight - leftHeight;
}
return 0;
}
But it was incurring a lot of performance overhead.
Performance of an AVL Tree in C#
So I went for storing the height value in each node at the time of insertion in the BinarySearchTree<T>. Now during balancing I am able to avoid this iteration and I am gaining the desired performance in AVLTree<T>.
But now the problem is if I try to insert a large number of data say 1-50000 sequentially in BinarySearchTree<T> (without balancing it), I am getting StackoverflowException. I am providing the code which is causing it. Can you please help me to find a solution which will avoid this exception and also not compromise with the performance in its child class AVLTree<T>?
public class BinaryTreeNode<T>
{
private BinaryTreeNode<T> _left, _right;
private int _height;
public T Value {get; set; }
public BinaryTreeNode<T> Parent;
public int Depth {get; set; }
public BinaryTreeNode()
{}
public BinaryTreeNode(T data)
{
Value = data;
}
public BinaryTreeNode<T> Left
{
get { return _left; }
set
{
_left = value;
if (_left != null)
{
_left.Depth = Depth + 1;
_left.Parent = this;
}
UpdateHeight();
}
}
public BinaryTreeNode<T> Right
{
get { return _right; }
set
{
_right = value;
if (_right != null)
{
_right.Depth = Depth + 1;
_right.Parent = this;
}
UpdateHeight();
}
}
public int Height
{
get { return _height; }
protected internal set
{
_height = value;
if (Parent != null) {
Parent.UpdateHeight();
}
}
}
private void UpdateHeight()
{
if (Left == null && Right == null) {
return;
}
if(Left != null && Right != null)
{
if (Left.Height > Right.Height)
Height = Left.Height + 1;
else
Height = Right.Height + 1;
}
else if(Left == null)
Height = Right.Height + 1;
else
Height = Left.Height + 1;
}
}
public class BinarySearchTree<T>
{
private readonly Comparer<T> _comparer = Comparer<T>.Default;
public BinarySearchTree()
{
}
public BinaryTreeNode<T> Root {get; set;}
public virtual void Add(T value)
{
var n = new BinaryTreeNode<T>(value);
int result;
BinaryTreeNode<T> current = Root, parent = null;
while (current != null)
{
result = _comparer.Compare(current.Value, value);
if (result == 0)
{
parent = current;
current = current.Left;
}
if (result > 0)
{
parent = current;
current = current.Left;
}
else if (result < 0)
{
parent = current;
current = current.Right;
}
}
if (parent == null)
Root = n;
else
{
result = _comparer.Compare(parent.Value, value);
if (result > 0)
parent.Left = n;
else
parent.Right = n;
}
}
}
I am getting the StackoverflowException in calculating the height at the following line
if (Parent != null) {
Parent.UpdateHeight();
}
in the Height property of BinaryTreeNode<T> class. If possible please suggest me some work around.
BTW, thanks a lot for your attention to read such a long question :)
When you add a node you compute the height by iterating over all the parent nodes recursively. A .NET process has limited stack space and given a big tree you will consume all stack space and get a StackOverflowException. You can change the recursion into an iteration to avoid consuming stack space. Other languages like functional languages are able to recurse without consuming stack space by using a technique called tail recursion. However, in C# you will have to manually modify your code.
Here are modified versions of Height and UpdateHeight in BinaryTreeNode<T> that doesn't use recursion:
public int Height {
get { return _height; }
private set { _height = value; }
}
void UpdateHeight() {
var leftHeight = Left != null ? Left.Height + 1 : 0;
var rightHeight = Right != null ? Right.Height + 1 : 0;
var height = Math.Max(leftHeight, rightHeight);
var node = this;
while (node != null) {
node.Height = height;
height += 1;
node = node.Parent;
}
}
You can add a tail. call in il, decompile the file and then compile it again.
Example:
.... IL_0002: add
tail.
IL_0003: call ...
IL_0008: ret
Example on compiling it again:
ilasm C:\test.il /out=C:\TestTail.exe
(this is probably not what you want, but again it's just an example)
I'm sure you can figure it out and make it work, it's not to hard.
The big downside is that recompilation will get rid of your tail call so I recommend to set up a build task in msbuild to do it automatically for you.
I think I found the solution, I modified the code as follows and it worked like a charm
public int Height
{
get { return _height; }
protected internal set
{
_height = value;
}
}
private void UpdateHeight()
{
if (Left == null && Right == null) {
return;
}
if(Left != null && Right != null)
{
if (Left.Height > Right.Height)
Height = Left.Height + 1;
else
Height = Right.Height + 1;
}
else if(Left == null)
Height = Right.Height + 1;
else
Height = Left.Height + 1;
var parent = Parent;
while (parent != null) {
parent.Height++;
parent = parent.Parent;
}
}
Thanks a lot guys who spend some time for me to tried to find out the solution.
If you are inserting large amounts of data in one go I would think you'd be better of batch inserting the data without the call to Parent.UpdateHeight then walk the tree setting the height as you go.
Adding future nodes I would walk the tree, starting at the root, incrementing the height as you go.