none
建置 Silverlight 1.0 開發環境 RRS feed

  • 常规讨论

  • 摘至网络

    MSN SpaceGoogle DocGoogle Blog
    Chui-Wen Chiu
    2007.07.26
    下載
    1. Sliverlight 1.0 Beta Runtime
    2. Sliverlight SDK 1.0 beta
    3. Microsoft Blend 2.0 Preview
    4. Sliverlight SDK 1.1 alpha
    安裝 Sliverlight 專案樣板
    為了讓 VS 2005 能夠開發 Sliverlight,先下載 Sliverlight SDK(Silverlight1.0SDK.zip),取出其中的 SilverlightBetaToolsForVS2005.zip,將內容解開後放置在 %My Document%\Visual Studio 2005\Templates\ProjectTemplates\Sliverlight 1.0 beta,此時 VS2005 中可以使用 Sliverlight 專案,如下圖:

    安裝 XAML IntelliSense
    從 Silverlight1.0SDK.zip 中取出 silverlight.xsd,並放置在 C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas 目錄下。
    專案樣板程式碼研究
    使用 Sliverlight 1.0 Beta 專案樣板產生的專案共會產生下面五個檔案:

    Default.html -- HTML UI 定義

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>SilverlightJSApplication1</title>
        <script type="text/javascript" src="Silverlight.js"></script>
        <script type="text/javascript" originalAttribute="src" originalPath="Silverlight.js"></script>
        <script type="text/javascript" src="Default.html.js"></script>
        <script type="text/javascript" originalAttribute="src" originalPath="Default.html.js"></script>
        <script type="text/javascript" src="Scene.xaml.js"></script> originalAttribute="src" originalPath=""Scene.xaml.js"></script>"
    </head>
    <body>
        <div id="SilverlightControlHost">
            <script type="text/javascript">
    createSilverlight(); // 建立 Sliverlight
            </script>
        </div>
    </body>
    </html>

    Default.html.js -- HTML 事件處理

    /**
    * 建立 Sliverlight
    *
    */
    function createSilverlight()
    {
        var scene = new SilverlightJSApplication1.Scene();
        Sys.Silverlight.createObjectEx({
            source: "Scene.xaml",
            parentElement: document.getElementById("SilverlightControlHost"),
            id: "SilverlightControl",
            properties: {
                width: "400",
                height: "400",
                version: "0.9"
            },
            events: {
                onLoad: Sys.Silverlight.createDelegate(scene, scene.handleLoad)
            }
        });
    }
    if (!window.Sys)
        window.Sys = {};
    if (!window.Silverlight)
        window.Silverlight = {};
    /**
    * 產生 create Delegate
    *
    * @param  instance   Scense 物件
    * @param  method    instance 的 method
    * @return  Function  呼叫 instance 的 member function 的 Function Object
    */
    Sys.Silverlight.createDelegate = function(instance, method) {
        return function() {
            return method.apply(instance, arguments);
        }
    }

    Scense.xaml -- XAML UI 定義

    <Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <Canvas.Resources>
        <Storyboard x:Name="mouseEnter">
          <ColorAnimation Duration="00:00:00.25" To="#3DFFFFFF" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
        </Storyboard>
        <Storyboard x:Name="mouseDown">
          <ColorAnimation Duration="00:00:00.2" To="#22000000" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
        </Storyboard>
        <Storyboard x:Name="mouseUp">
          <ColorAnimation Duration="00:00:00.2" To="#3DFFFFFF" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
        </Storyboard>
        <Storyboard x:Name="mouseLeave">
          <ColorAnimation Duration="00:00:00.25" To="#00FFFFFF" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
        </Storyboard>   
      </Canvas.Resources>
      <Canvas Width="120" Height="44">
        <Rectangle StrokeThickness="4" RadiusX="17" RadiusY="36" Width="120" Height="44" Stroke="#46000000">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.5,-0.409" StartPoint="0.5,1.409">
              <GradientStop Color="#FFD3BE46" Offset="0.242"/>
              <GradientStop Color="#FFD79B03" Offset="0.333"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
    <TextBlock Width="67" Height="23.2" Canvas.Left="29" Canvas.Top="10" Foreground="#FFEFEFEF" Text="Click Me" />
        <Rectangle StrokeThickness="4" RadiusX="16" RadiusY="36" Width="104" Height="32" Canvas.Left="8" Canvas.Top="1.3">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.5,-0.409" StartPoint="0.5,1.409">
              <GradientStop Color="#00FFFFFF" Offset="0.13"/>
              <GradientStop Color="#FFFFFFFF" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <Rectangle RadiusX="17" RadiusY="36" Width="114" Height="38" Fill="#00FFFFFF" x:Name="highlightEllipse" Canvas.Left="3" Canvas.Top="3"/>
      </Canvas>
    </Canvas>

    Scense.xaml.js -- XAML 事件處理

    if (!window.SilverlightJSApplication1)
        window.SilverlightJSApplication1 = {};
    SilverlightJSApplication1.Scene = function()
    {
    }
    SilverlightJSApplication1.Scene.prototype =
    {
        /**
         * Sliverlight 事件載入完成的事件處理函式
         *
         * @param   control
         * @param   userContext
         * @param   rootElement 
         */
        handleLoad: function(control, userContext, rootElement)
        {
            this.control = control;
            // Sample button event hookup: Find the button and then attach event handlers
            this.button = rootElement.children.getItem(0);   
            // 建立事件處理
            this.button.addEventListener("MouseEnter", Sys.Silverlight.createDelegate(this, this.handleMouseEnter));
            this.button.addEventListener("MouseLeftButtonDown", Sys.Silverlight.createDelegate(this, this.handleMouseDown));
            this.button.addEventListener("MouseLeftButtonUp", Sys.Silverlight.createDelegate(this, this.handleMouseUp));
            this.button.addEventListener("MouseLeave", Sys.Silverlight.createDelegate(this, this.handleMouseLeave));
        },
        /**
         * 處理 Mouse Enter 事件
         *
         * @param sender
         * @param eventArgs
         */
        handleMouseEnter: function(sender, eventArgs)
        {
            var mouseEnterAnimation = sender.findName("mouseEnter");
            mouseEnterAnimation.begin();
        },
        /**
         * 處理 Mouse Down 事件
         *
         * @param sender
         * @param eventArgs
         */   
        handleMouseDown: function(sender, eventArgs)
        {
            var mouseDownAnimation = sender.findName("mouseDown");
            mouseDownAnimation.begin();
        },
        /**
         * 處理 Mouse Up 事件
         *
         * @param sender
         * @param eventArgs
         */  
        handleMouseUp: function(sender, eventArgs)
        {
            var mouseUpAnimation = sender.findName("mouseUp");
            mouseUpAnimation.begin();
            // Put clicked logic here
            alert("clicked");
        },
        /**
         * 處理 Mouse Leave 事件
         *
         * @param sender
         * @param eventArgs
         */   
        handleMouseLeave: function(sender, eventArgs)
        {
            var mouseLeaveAnimation = sender.findName("mouseLeave");
            mouseLeaveAnimation.begin();
        }
    }

    Sliverlight.js -- Sliverlight 核心

    /**
    *  Silverlight.js   version 0.9
    *
    *  This file is provided by Microsoft as a helper file for websites that
    *  incorporate Silverlight Objects.  It must be used in conjunction with createSilverlight.js,
    *  or alternatively, a custom .js file specific to your site.  The 0.9 version of this file is
    *  hard coded to match Microsoft Silverlight v1.0 Beta, which exposes 0.9 as its version number.   
    *  This file is provided as is.
    *
    */
    if (!window.Sys)
    {
       window.Sys = { };
    }
    if (!window.Sys.Silverlight)
    {
        window.Sys.Silverlight = { };
    }
    /**
    * 檢查是否安裝符合指定的 Sliverlight 版本
    *
    * @param   version      版本
    * @return                  true=已安裝, false=未安裝
    */
    Sys.Silverlight.isInstalled = function(version)
    {
        var uaString = navigator.userAgent;
        var reqVersionArray = version.split(".");
        reqMajorVer = (reqVersionArray[0] != null) ? reqVersionArray[0] : 0;
        reqMinorVer = (reqVersionArray[1] != null) ? reqVersionArray[1] : 9;
        reqBuildVer = (reqVersionArray[2] != null) ? reqVersionArray[2] : 0;    
        /**
         * 取得 Sliverlight 版本資訊
         *
         * @return 版本資訊
         */
        function detectAgControlVersion()
        {
            agVersion = -1;      
            if ((navigator.plugins != null) && (navigator.plugins.length > 0))
            {       
            if (document.getElementById && !document.all && navigator.plugins["WPFe Plug-In"] )
            {
                if (navigator.userAgent.indexOf("Firefox") != -1)
                {
                    // ??
                    var tmpAgObjectTag = '<object id="tmpSilverlightVersion" width="1" height="1" type="application/ag-plugin"/>';
                    range = document.createRange();   
                    range.selectNode(document.body);           
                    range.setStartBefore(document.body);
                    tmpAgControlDiv = document.createElement('DIV');
                    document.body.appendChild(tmpAgControlDiv);
                    tmpAgControlDiv.innerHTML=tmpAgObjectTag;
    agVersionElement=document.getElementById("tmpSilverlightVersion");
    agVersion=agVersionElement.settings.version;
                    document.body.removeChild(tmpAgControlDiv);
                }
                else
                {
    agVersion = navigator.plugins["WPFe Plug-In"].description;
                }
            }
            }
            else if ((navigator.userAgent.indexOf('Windows') != -1) && (navigator.appVersion.indexOf('MSIE') != -1) )
            {
                try
                {
    var AgControl = new ActiveXObject("AgControl.AgControl");    
    agVersion = AgControl.settings.version;  
                    AgControl = null;
                }
                catch (e)
                {
                    agVersion = -1;
                }
            }
            return agVersion;
        }
        var versionStr = detectAgControlVersion();
        if (versionStr == -1 )
        {
            return false;
        }
        else if (versionStr != 0)
        {
            versionArray = versionStr.split(".");
            var versionMajor = versionArray[0];
            var versionMinor = versionArray[1];
            var versionBuild = versionArray[2];
            if (versionMajor > parseFloat(reqMajorVer))
            {
                return true;
            }
            else if (versionMajor == parseFloat(reqMajorVer))
            {
                if (versionMinor > parseFloat(reqMinorVer))
                {
                    return true;
                }
                else if (versionMinor == parseFloat(reqMinorVer))
                {
                    if (versionBuild >= parseFloat(reqBuildVer))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }
    /**
    * Silverlight event instance counter for memory mgt
    *
    */
    Sys.Silverlight._counterL = 0;
    /**
    * 建立 Sliverlight
    *
    * @param   source          xaml 檔案來源
    * @param   id                <object> 標籤的識別 id
    * @param   properties      屬性集合,格式為 { name:value, name:value, name:value}
    * @param   events          事件集合,格式為 { name:value, name:value, name:value}
    * @param   initParams      <param> 參數設定,格式為 { name:value, name:value, name:value}
    * @param   userContext  
    * @return                       Plugin 字串
    */
    Sys.Silverlight.createObject = function(source, parentElement, id, properties, events, initParams, userContext)
    {
        var slPluginHelper = new Object();
        var slProperties = properties;       
        var slEvents = events;
        slPluginHelper.source = source;
        slPluginHelper.parentElement = parentElement;
        slPluginHelper.id = id;        
        slPluginHelper.width = slProperties.width;
        slPluginHelper.height = slProperties.height;
        slPluginHelper.background = slProperties.background;       
        slPluginHelper.isWindowless = slProperties.isWindowless;
        slPluginHelper.framerate = slProperties.framerate;
        slPluginHelper.ignoreBrowserVer = slProperties.ignoreBrowserVer;   
        slPluginHelper.inplaceInstallPrompt = slProperties.inplaceInstallPrompt;
        slPluginHelper.enableHtmlAccess = slProperties.enableHtmlAccess;
        slPluginHelper.initParams = initParams;       
        //memory management for onLoad event     
        if (slEvents.onLoad)
        {
           var uniqueID = '_sl' + (Sys.Silverlight._counterL++);
           slPluginHelper.loadedHandlerName = 'javascript:' + uniqueID; 
           function _dispose()
           {
            if (window.detachEvent)
            {
                window.detachEvent('onunload', _dispose);
            }
            else
            {
                window.removeEventListener('unload', _dispose, false);
            }
            window[uniqueID] = null;
           }
            function _loadedHandler(sender)
            {
                slEvents.onLoad(document.getElementById(slPluginHelper.id), userContext, sender);
                _dispose();
            }
            window[uniqueID] = _loadedHandler; 
            if (window.attachEvent)
            {
                window.attachEvent('onunload', _dispose);
            }
            else
            {
                window.addEventListener('unload', _dispose, false);
            }
        }
        //set error handler
        if (!slEvents.onError)
        {
            slPluginHelper.onError = "default_error_handler";
        }
        else
        {
            slPluginHelper.onError = slEvents.onError;
        }         
        var slPluginHTML = "";
        //direct download pointer
        var directDownload;
        if (navigator.userAgent.indexOf('Windows') != -1)
        {
    directDownload = "http://go.microsoft.com/fwlink/?LinkID=86008";
        }
        else if (navigator.userAgent.indexOf('PPC Mac OS X') != -1)
        {
    directDownload = "http://go.microsoft.com/fwlink/?LinkID=87380";
        }
        else if (navigator.userAgent.indexOf('Intel Mac OS X') != -1)
        {
    directDownload = "http://go.microsoft.com/fwlink/?LinkID=87384";
        }
        //point to correct image/landing page for Alpha (0.95.x) and Beta (0.90.x)
        var inDirectDownloadPage, inDirectDownloadImage;
        var curVer = slProperties.version.split(".");
            majorVer = curVer[0];
            minorVer = curVer[1];
        //if Alpha, disallow inPlaceInstall
        if (minorVer == "95")
        {
            slPluginHelper.inplaceInstallPrompt = false;
    inDirectDownloadPage = "http://go.microsoft.com/fwlink/?LinkID=88363";
    inDirectDownloadImage = "http://go.microsoft.com/fwlink/?LinkID=88365";
        }
        else
        {
    inDirectDownloadPage = "http://go.microsoft.com/fwlink/?LinkID=86009";
    inDirectDownloadImage = "http://go.microsoft.com/fwlink/?LinkID=87023";
        }
        // text for Silverlight image link, used for non-inplaceInstallPrompt and unsupported browser
        var silverlightLink = '<div style="width: 205px; height: 67px; background-color: #FFFFFF"><a href="'+inDirectDownloadPage+'"><img style="border:0";  src="'+inDirectDownloadImage+'"/></a></div>'
        // detect supported browser version & that the correct version of WPF/e is installed, else display install
        if (browserIsSupportedVersion(slPluginHelper))
        {  
            if (Sys.Silverlight.isInstalled(slProperties.version))
            {
                slPluginHTML = buildHTML(slPluginHelper);
            }
            else if (!slPluginHelper.inplaceInstallPrompt)
            {
                slPluginHTML = silverlightLink;
            }
            else  //inPlaceInstallPrompt
            {
                slPluginHTML += '<div style="width: 205px; height: 101px background-color: #FFFFFF;"><a href="'+directDownload+'"><img style="border:0"; SRC="http://go.microsoft.com/fwlink/?LinkID=87024"></a>';               
                slPluginHTML += '<div style="margin-top: -60px;text-align: center;color: #FFFFFF; font-size: 10px;font-family: Arial ">By clicking <b>Get Microsoft Silverlight</b> you accept the ';
                slPluginHTML += '<a href="http://go.microsoft.com/fwlink/?LinkID=87025" originalAttribute="href" originalPath="'+inDirectDownloadPage+'"><img style="border:0";  src="'+inDirectDownloadImage+'"/></a></div>'
        // detect supported browser version & that the correct version of WPF/e is installed, else display install
        if (browserIsSupportedVersion(slPluginHelper))
        {  
            if (Sys.Silverlight.isInstalled(slProperties.version))
            {
                slPluginHTML = buildHTML(slPluginHelper);
            }
            else if (!slPluginHelper.inplaceInstallPrompt)
            {
                slPluginHTML = silverlightLink;
            }
            else  //inPlaceInstallPrompt
            {
                slPluginHTML += '<div style="width: 205px; height: 101px background-color: #FFFFFF;"><a href="'+directDownload+'"><img style="border:0"; SRC="http://go.microsoft.com/fwlink/?LinkID=87024"></a>';               
                slPluginHTML += '<div style="margin-top: -60px;text-align: center;color: #FFFFFF; font-size: 10px;font-family: Arial ">By clicking <b>Get Microsoft Silverlight</b> you accept the ';
                slPluginHTML += '<a href="http://go.microsoft.com/fwlink/?LinkID=87025" style="text-decoration: underline;color: #FFFFFF;">Silverlight license agreement.</a></div>';               
                slPluginHTML += '<div style="margin-top: 8px;text-align: center;color: #FFFFFF; font-family: Arial; font-size: 10px;">Silverlight updates automatically, <a href="http://go.microsoft.com/fwlink/?LinkID=87026" originalAttribute="href" originalPath="http://go.microsoft.com/fwlink/?LinkID=87026" style="text-decoration: underline;color: #FFFFFF;">learn more.</a></div></div>';
            }
        }
        else
        {
            slPluginHTML = silverlightLink;
        }       
        // insert the HTML into the requested host element or return <object> tag.
        if(parentElement != null)
        {
            parentElement.innerHTML = slPluginHTML;
        }
        else
        {
            return slPluginHTML;
        }
    }
    /**
    *  檢測瀏覽器是否支援 Sliverlight
    *
    * @param   slPluginHelper     Plugin 參數
    * @return                         true=支援, false=不支援
    */
    function browserIsSupportedVersion(slPluginHelper)
    {
        var supportedBrowser = true;
        if (slPluginHelper.ignoreBrowserVer == true)
        {
            return supportedBrowser;
        }
        else
        {   
            var supportedBrowser = false;         
        }    
        // detection for Internet Explorer 6.0+, 32-bit only
        if (navigator.userAgent.indexOf('MSIE') != -1)
        {
            if (navigator.userAgent.indexOf('Win64') == -1)
            {          
                var tempVersion = navigator.userAgent.split("MSIE");
                browserMajorVersion = parseInt(tempVersion[1]);
                    if (browserMajorVersion >= 6.0)
                    {
                        supportedBrowser = true;
                    }
            }
        }
        // detection for Firefox 1.5+ and 2.0
        else if (navigator.userAgent.indexOf("Firefox") != -1)
        {
            var tempVersion = navigator.userAgent.split("Firefox/");
            tempVersion = tempVersion[1].split(".");
            browserMajorVersion = parseFloat(tempVersion[0]);
            browserMinorVersion = parseFloat(tempVersion[1]);
            if (browserMajorVersion >= 2)
            {
                supportedBrowser = true;
            }
            else
            {
                if ((browserMinorVersion >= 5))
                {
                    supportedBrowser = true;
                }
            }
        }
        else if (navigator.userAgent.indexOf("Safari") != -1)
        {
            supportedBrowser = true;
        }
        return supportedBrowser;
    }
    /**
    *  建立控制像實體的 HTML
    *
    *  預設範例動態產生如下的 HTML
    *  <OBJECT id="SilverlightControl" type="application/ag-plugin" height="400" width="400">
    *      <PARAM value="Scene.xaml" name="source" />
    *      <PARAM value="default_error_handler" name="onError" />
    *      <PARAM value="javascript:_sl0" name="onLoad" />
    *  </OBJECT>
    *
    * @param   slPluginHelper     Plugin 參數
    * @return                          HTML 字串
    */
    function buildHTML(slPluginHelper)
    {
        var slPluginHTML = '<object type="application/ag-plugin" id="'+slPluginHelper.id+'" width="'+slPluginHelper.width+'" height="'+slPluginHelper.height+'" >';
        if (slPluginHelper.source != null)
        {
            slPluginHTML += ' <param name="source" value="'+slPluginHelper.source+'" />';
        }
        if (slPluginHelper.framerate != null)
        {
            slPluginHTML += ' <param name="maxFramerate" value="'+slPluginHelper.framerate+'" />';
        }
        slPluginHTML += ' <param name="onError" value="'+slPluginHelper.onError+'" />';      
        if (slPluginHelper.background != null)
        {
            slPluginHTML += ' <param name="background" value="'+slPluginHelper.background+'" />';
        }
        if (slPluginHelper.isWindowless != null)
        {
            slPluginHTML += ' <param name="windowless" value="'+slPluginHelper.isWindowless+'" />';       
        }
        if (slPluginHelper.initParams != null)
        {
            slPluginHTML += ' <param name="initParams" value="'+slPluginHelper.initParams+'" />';       
        }
        if (slPluginHelper.enableHtmlAccess != null)
        {
            slPluginHTML += ' <param name="enableHtmlAccess" value="'+slPluginHelper.enableHtmlAccess+'" />';       
        }
        if (slPluginHelper.loadedHandlerName != null)
        {
            slPluginHTML += ' <param name="onLoad" value="'+slPluginHelper.loadedHandlerName+'" />';       
        }
        slPluginHTML += '<\/object>';
        if (navigator.userAgent.indexOf("Safari") != -1)
       {
            // disable Safari caching
            // for more information, see http://developer.apple.com/internet/safari/faq.html#anchor5
            slPluginHTML += "<iframe style='visibility:hidden;height:0;width:0'/>";
       }
        return slPluginHTML;
    }
    /**
    *  預設錯誤處理函式
    *
    * @param   sender
    * @param   args
    */
    function default_error_handler(sender, args)
    {
        var iErrorCode;
        var errorType = args.ErrorType;
        iErrorCode = args.ErrorCode;
        var errMsg = "\nSilverlight error message     \n" ;
        errMsg += "ErrorCode: "+ iErrorCode + "\n";
        errMsg += "ErrorType: " + errorType + "       \n";
        errMsg += "Message: " + args.ErrorMessage + "     \n";
        if (errorType == "ParserError")
        {
            errMsg += "XamlFile: " + args.xamlFile + "     \n";
            errMsg += "Line: " + args.lineNumber + "     \n";
            errMsg += "Position: " + args.charPosition + "     \n";
        }
        else if (errorType == "RuntimeError")
        {          
            if (args.lineNumber != 0)
            {
                errMsg += "Line: " + args.lineNumber + "     \n";
                errMsg += "Position: " +  args.charPosition + "     \n";
            }
            errMsg += "MethodName: " + args.methodName + "     \n";
        }
        alert(errMsg);
    }
    /**
    * createObjectEx, takes a single parameter of all createObject parameters enclosed in {}      
    *
    * @param   params
    * @return
    */
    Sys.Silverlight.createObjectEx = function(params)
    {       
        var parameters = params;
        var html = Sys.Silverlight.createObject(parameters.source, parameters.parentElement, parameters.id, parameters.properties, parameters.events, parameters.initParams, parameters.context);
        if (parameters.parentElement == null)
        {
            return html;
        }
    }

    執行結果
    [Firefox 2.0.0.5]

    [IE 6]


    補充
    1. Sliverlight 元件的 ProgID 為 AgControl.AgControl,元件檔案位於 C:\Program Files\Microsoft Silverlight\npctrl.dll,CLASS_ID 為 {32C73088-76AE-40F7-AC40-81F62CB2C1DA}


    My blog: http://blog.csdn.net/dotfun http://dotfun.cnblogs.com

    My contact: QQ:372900288 E-mail:372900288@qq.com msn:sellnet007@hotmail.com

    2009年3月9日 6:27
    版主