我一直在关注编写自定义标记助手here的Microsoft文章。
每个我看到C#
中硬编码元素标记的代码示例(取自上述链接)
#'sigCOMP
#'@description signal comparison operators incl and, or, xor for quantstrat signals.
#'@param label name of the output signal
#'@param data the market data
#'@param columns the signal columns to intersect, if a second level comparison is used, the comparison result must reside in the first column only (compare one 2nd level with a True/False Column) or in both, marked by Keyword '2nd'
#'@param relationship operators gte, gt, lte, lt, eq, and, or, xor TODO:NOT
#'@param secondComparison vector of columns to intersect, if yes, then also set the relationship comparison
#'@param relationshipSecondComparison operators gte, gt, lte, lt, eq
#'@param offset1 optional
#'@param offset2 optional
#'@return a new signal column that intersects the provided columns
#'@export
sigCOMP <- function (label, data = mktdata, columns, relationship = c("gte", "gt", "lte", "lt", "eq", "and", "or", "xor"), relationshipSecondComparison = c("gte", "gt", "lte", "lt", "eq"), secondComparison, res_not, offset1 = 0, offset2 = 0)
{
ret_sig = NULL
compcols <- NULL
if(!missing(columns)){
if (relationship == "op") {
if (columns[1] %in% c("Close", "Cl", "close"))
stop("Close not supported with relationship=='op'")
switch(columns[1], Low = , low = , bid = {
relationship = "lt"
}, Hi = , High = , high = , ask = {
relationship = "gt"
})
} #whatever that is
colNums <- NULL
for(sec in 1:length(columns)){
if (columns[sec]=='2nd'){
colNums <- c(colNums,0)
}
else{
colNums <- c(colNums, match.names(columns[sec], colnames(data)))
}
}
opr <- switch(relationship[1],
gt = , `>` = ">",
gte = , gteq = , ge = , `>=` = ">=",
lt = , `<` = "<",
lte = , lteq = , le = , `<=` = "<=",
eq = , `==` = , `=` = "==",
and = "&",
or = "|",
xor = "xor"
# todo: NOT
)
} #perform preparation actions if 1|2 columns exist or else stop
else {
stop("only works if two comparison columns are provided. for true/false evaluations you can add e.g. 2nd 2nd or <Signal>, 2nd ")
}
if (!missing(secondComparison))
{
ret_sig2nd <- NULL
opr2nd <- c(1:length(secondComparison))
if (length(secondComparison) != length(relationshipSecondComparison)){
stop("make sure to have a comparison operator for each second level comparison you would like to perform")
}
else {
for (j in 1:length(relationshipSecondComparison)) {
# run through pairs of columns and relationship checks and return these in a dataframe ret_sig2nd
# the return column of the appropriate pair will have the name col1 op col2 e.g. close gt nFast
colNums2nd <- c(0,0)
comp2ndPartners <- unlist(secondComparison[j])
relationship2 <- unlist(relationshipSecondComparison)[j]
colNums2nd[1] <- match.names(comp2ndPartners[1], colnames(data))
colNums2nd[2] <- match.names(comp2ndPartners[2], colnames(data))
opr2nd[j] <- switch(relationship2,
gt = , `>` = ">",
gte = , gteq = , ge = , `>=` = ">=",
lt = , `<` = "<",
lte = , lteq = , le = , `<=` = "<=",
eq = , `==` = , `=` = "==",
and = "&",
or = "|",
xor = "xor"
# todo: NOT
)
ret_append <- do.call(opr2nd[j], list(data[, colNums2nd[1]] + offset1,
data[, colNums2nd[2]] + offset2))
colnames(ret_append) <- paste0(comp2ndPartners[1]," ",relationship2[j]," ",comp2ndPartners[2])
ret_sig2nd <- cbind(ret_sig2nd,ret_append)
rm(ret_append)
}
compcols <- ret_sig2nd
} # end of 2nd Comp = 2nd Relationship validity block
if(ncol(compcols)==1){ # check the case if only one second level comparison exists
transfer2ndToFirst <- compcols #assumption is, the second level comparison took place with the first column of the first level
# if one second level comparison is provided, execute transfer object with second column of first level
compcols <- transfer2ndToFirst[, 1] #offset already included in second level comparison
compcols <- cbind(compcols, data[, colNums[2]] + offset2)
} # provide the transfer object to be used in the first level comparison if only one second level comparison exists
}
else { # check the case if no second level comparison exists
# if no second level comparison is provided, only execute first level
compcols <- data[, colNums[1]] + offset1
compcols <- cbind(compcols, data[, colNums[2]] + offset2)
} # if no second level exists, execute comparison for first level only
# for all cases, perform the first level comparison with the columns stored in compcols - offset has to be applied before storing to compcols
ret_sig <- do.call(opr, list(compcols[, 1] ,
compcols[, 2] ))
colnames(ret_sig) <- label
return(ret_sig)
}
# ### TESTS
# # To compare just two (first level) colums
# rm(testOnlyFirst)
# testOnlyFirst<- sigCOMP(
# columns=c("nSlow","nFast"),
# relationship=c("gt"),
# label='GT'
# )
#
#
# #To compare a signal or another T/F value with a second level comparison
# rm(testOneSecond)
# testOneSecond<- sigCOMP(
# columns=c("2nd","exitLong"),
# relationship=c("and"),
# secondComparison =list(c("Close", "nFast")),
# relationshipSecondComparison = list(c("gt")),
# label='andGT'
# )
#
#
# rm(test2Second)
# test2Second<- sigCOMP(
# columns=c("2nd", "2nd"),
# relationship=c("or"),
# secondComparison =list(c("Close", "nFast"), c("Close", "nSlow")),
# relationshipSecondComparison = list(c("gt"), c("gt")),
# label='orGT'
# )
#
# rm(test2SecondOr)
# test2SecondOr<- sigCOMP(
# columns=c("2nd", "2nd"),
# relationship=c("or"),
# secondComparison =list(c("Close", "nFast"), c("Close", "nSlow")),
# relationshipSecondComparison = list(c("gt"), c("gt")),
# label='orGT'
# )
#
# rm(test2SecondXor)
# test2SecondXor<- sigCOMP(
# columns=c("2nd", "2nd"),
# relationship=c("xor"),
# secondComparison =list(c("Close", "nFast"), c("Close", "nSlow")),
# relationshipSecondComparison = list(c("gt"), c("gt")),
# label='orGT'
# )
有没有一种方法可以从cshtml文件中加载标记模板? (类似于加载部分视图)
我的目的是拥有单独的public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "section";
output.Content.SetHtmlContent(
$@"<ul><li><strong>Version:</strong> {Info.Version}</li>
<li><strong>Copyright Year:</strong> {Info.CopyrightYear}</li>
<li><strong>Approved:</strong> {Info.Approved}</li>
<li><strong>Number of tags to show:</strong> {Info.TagsToShow}</li></ul>");
output.TagMode = TagMode.StartTagAndEndTag;
}
个文件(每个元素类型一个),这样我就可以轻松设置它们的样式。我的C#看起来也很干净!
谢谢,
詹姆斯
答案 0 :(得分:1)
您可以创建部分视图,并从TagHelper
课程中调用它。例如:
<!-- Views/Shared/TagHelpers/MyList.cshtml -->
@model YourInfoClass
<ul>
<li><strong>Version:</strong> @Model.Version</li>
<li><strong>Copyright Year:</strong> @Model.CopyrightYear</li>
<li><strong>Approved:</strong> @Model.Approved</li>
<li><strong>Number of tags to show:</strong> @Model.TagsToShow</li>
</ul>
在TagHelper
:
[HtmlTargetElement("mylist")]
public class MyListTagHelper : TagHelper
{
private HtmlHelper _htmlHelper;
private HtmlEncoder _htmlEncoder;
public MyListTagHelper(IHtmlHelper htmlHelper, HtmlEncoder htmlEncoder)
{
_htmlHelper = htmlHelper as HtmlHelper;
_htmlEncoder = htmlEncoder;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "section";
output.TagMode = TagMode.StartTagAndEndTag;
var partial = await _htmlHelper.PartialAsync("TagHelpers/MyList", Info);
var writer = new StringWriter();
partial.WriteTo(writer, _htmlEncoder);
output.Content.SetHtmlContent(writer.ToString());
}
}
答案 1 :(得分:0)
我已经实现了类似这样的东西,但它通过创建一个带依赖注入的自定义ViewRendering服务并使用它来将视图呈现为字符串,从而采用了一种方法。我喜欢这种方法,因为它允许我的应用程序使用视图来处理许多事情,包括电子邮件模板,标记,以及我需要将视图呈现为字符串以供在代码中使用的任何其他情况,同时还允许我传递模型到更多动态元素的视图。
定义服务的接口:
public interface IViewRenderService
{
string RenderView(string viewName);
string RenderView<TModel>(string viewName, TModel model);
}
服务的实施(重要部分):
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using System;
using System.IO;
public class ViewRenderService : IViewRenderService
{
private readonly IRazorViewEngine _viewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
public ViewRenderService(IRazorViewEngine viewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
public string RenderView(string viewName)
{
var actionContext = GetActionContext();
var viewEngineResult = _viewEngine.FindView(actionContext, viewName, false);
if (!viewEngineResult.Success)
{
throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", viewName));
}
var view = viewEngineResult.View;
using (var output = new StringWriter())
{
var viewContext = new ViewContext(
actionContext,
view,
new ViewDataDictionary(
metadataProvider: new EmptyModelMetadataProvider(),
modelState: new ModelStateDictionary()),
new TempDataDictionary(
actionContext.HttpContext,
_tempDataProvider),
output,
new HtmlHelperOptions());
view.RenderAsync(viewContext).GetAwaiter().GetResult();
return output.ToString();
}
}
public string RenderView<TModel>(string viewName, TModel model)
{
var actionContext = GetActionContext();
var viewEngineResult = _viewEngine.FindView(actionContext, viewName, false);
if (!viewEngineResult.Success)
{
throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", viewName));
}
var view = viewEngineResult.View;
using (var output = new StringWriter())
{
var viewContext = new ViewContext(
actionContext,
view,
new ViewDataDictionary<TModel>(
metadataProvider: new EmptyModelMetadataProvider(),
modelState: new ModelStateDictionary())
{
Model = model
},
new TempDataDictionary(
actionContext.HttpContext,
_tempDataProvider),
output,
new HtmlHelperOptions());
view.RenderAsync(viewContext).GetAwaiter().GetResult();
return output.ToString();
}
}
private ActionContext GetActionContext()
{
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = _serviceProvider;
return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
}
}
然后在你的Startup.cs
中 public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IViewRenderService, ViewRenderService>();
...
}
最后,你会在这样的代码中使用它(在这个例子中是一个控制器):
public class TestController : Controller
{
private IViewRenderService viewRenderService;
public TestController(IViewRenderService _viewRenderService)
{
viewRenderService = _viewRenderService;
}
public async Task<IActionResult> Index()
{
// code
var stringOfView = viewRenderService.RenderView("EmailTemplate/EmailConfirmation");
// code that does something with the view as a string
return View();
}
}
您可以将Views放在他们自己文件夹下的Views文件夹中(在视图路径上方的示例中是:/Views/EmailTemplate/EmailConfirmation.cshtml)
如果你的视图需要一个模型,你会像这样传递它:
var stringOfView = viewRenderService.RenderView("folder/view", model);
希望这有帮助!
答案 2 :(得分:0)
在Tech Dominator http://blog.techdominator.com/article/using-html-helper-inside-tag-helpers.html的这篇文章中,他们展示了如何以我发现的最简单的方法来做到这一点。
这是该文章中出现的示例。我测试了它,效果很好:
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;
namespace UsingCshtmlTemplatesInTagHelpers.TagHelpers
{
public class Holder
{
public string Name { get; set; }
}
public class TemplateRendererTagHelper : TagHelper
{
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
private IHtmlHelper _htmlHelper;
public TemplateRendererTagHelper(IHtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper;
}
public override async Task ProcessAsync(TagHelperContext context
, TagHelperOutput output)
{
(_htmlHelper as IViewContextAware).Contextualize(ViewContext);
/*
* Create some data that are going
* to be passed to the view
*/
_htmlHelper.ViewData["Name"] = "Ali";
_htmlHelper.ViewBag.AnotherName = "Kamel";
Holder model = new Holder { Name = "Charles Henry" };
output.TagName = "div";
/*
* model is passed explicitly
* ViewData and ViewBag are passed implicitly
*/
output.Content.SetHtmlContent(await _htmlHelper.PartialAsync("Template", model));
}
}
}